/* pgc_rc.c - A simple configuration file reader/writer source file
 * by Patrick Gallot <patrick.gallot@cimlinc.com>
 *
 * This file handles reading/writing of .gtkeyboardrc and .gtkeyboard-layout
 */
/* GTKeyboard - A Graphical Keyboard For X
 * Copyright (C) 1999, 2000
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 */

#define RC_FILE_C

#include "master.h"

/* Makes a NULL-terminated string that gets passed as input nothing but
 * upper case characters.
 * 
 * THIS FUNCTION MODIFIES ITS ARGUMENT
 */
void str_toupper(char *string)
{
     int x = 0;
     
     while(string[x]) /* Stop on NULL */
     {
	  string[x] = toupper(string[x]);
	  x++;
     } /* End while */
} /* End str_toupper() */

/* Passed - the filename, the set of variables to look for, (see the header
 * file for the definition of that structure) and an integer - if it's 0, 
 * then "syntax error" messages won't be printed.  Otherwise, we'll complain.
 */
int read_ConfigFile(char *filename, RCVARS *vars, int complain)
{
     gint n=0;
     gpointer N;
     int abool = OFF;
     FILE *rcFile;
     GHashTable *H;
     gchar Line[BUFSIZ], varPrefix[BUFSIZ], varName[BUFSIZ], varVal[BUFSIZ];
     
     /* if the RC file doesn't exist, don't bother doing anything else */
     if((rcFile = fopen( filename,"r")) == NULL)
     {
          if(complain)
          {
               fprintf(Q,"Couldn't open %s for reading: %s\n",
                       filename, g_strerror(errno));
               fflush(Q);
          } /* End if */
	  return 1;
     } /* End if */
     
     /* Create a hash table of all the variable names and their arrayindex+1 */
     H = g_hash_table_new(g_str_hash,g_str_equal);

     n = 0;
     /* Hash in all of the variable names.  Later when we read them in,
      * we'll hash in what we read to compare it against what was in the
      * table passed to us.
      */
     while(vars[n].Type != RC_NONE)
     {
	  g_hash_table_insert(H, vars[n].Name, GINT_TO_POINTER(n+1));	
	  n++;
     } /* End while */
     
     /* read each line of the RC file */
     while(fgets(Line,BUFSIZ,rcFile) != NULL)
     {
	  /* Strip leading and trailing whitespace from the string */
	  strcpy(Line, g_strstrip(Line));

#ifdef PARANOID_DEBUGGING
	  fprintf(Q,"Got Line \"%s\"\n",Line); fflush(Q);
#endif

	  /* Skip comments and lines too short to have useful information
	   * in them.  This will include "blank" lines that got g_strstrip'd
	   * down to 0 or 1 bytes
	   */
	  if((strlen(Line) < 2) || Line[0] == '#')
	       continue;

	  /* Initialize values so in case of error they will be NULL, not
	   * the value of the last parse.
	   */
	  varPrefix[0] = varName[0] = varVal[0] = '\0';

	  /* grab each variable and its value (maybe). 
	   * If prefix is specified, it tries to read the
	   * given prefix.
	   */
          if(strstr(Line,"="))
               sscanf(Line," %s = %s\n", varName, varVal);
          else
               sscanf(Line," %s %s %s\n",varPrefix, varName, varVal);

          /* Sometimes prefix *could* be null, but if name or value is
           * null, there's really nothing we can do with that string.
           */
          if(!varName[0] && !varVal[0])
          {
               if(complain)
               {
                    fprintf(stderr,"Error parsing line \"%s\": ", Line);
                    fprintf(stderr,"I have no idea what that line means.\n");
                    fflush(stderr);
               } /* End if */
               
               continue;
          } /* End if */

	  /* We want the rc file to be case insensitive, but we're looking for
	   * all upper-case varaible names, so convert the string to all
	   * upper-case so it will hash in correctly.
	   */
	  str_toupper(varName);

#ifdef PARANOID_DEBUGGING
          fprintf(Q,"Got \"%s\" = \"%s\"\n", varName, varVal);
          fflush(Q);
#endif /* PARANOID_DEBUGGING */

	  /* use the hash table to find the correct array entry */ 
	  if( (N = g_hash_table_lookup(H, varName)) == NULL)
	  {
               if(complain)
               {
                    fprintf(stderr,"Error parsing line \"%s\" -> ",
                            Line); 
                    fprintf(stderr,"Var name \"%s\" unknown.\n",
                            varName ? varName : "NULL");
                    fflush(stderr);
               } /* End if */
	       continue; /* but skip to the next line if can't find it. */
	  } /* End if */
	  
	  n = GPOINTER_TO_INT(N) - 1; /* convert back into an array index */

          /* We can't necessarily match the read prefix to the requested 
           * prefix since the prefixes may be different and may require
           * processing through the function pointer associated with the
           * record
           */

          /* 
           * Did we see a prefix when we didn't want one?
           */
          if(!vars[n].Prefix && varPrefix[0])
          {
               fprintf(stderr,
                       "Error:  Bad syntax.  I wasn't expecting to see ");
               fprintf(stderr,"a variable prefix on \"%s\".\n",
                       (varName ? varName : Line)); 
               fprintf(stderr,"Ignoring line \"%s\"\n",Line);
               fflush(stderr);
               continue;
          } /* End if */

	  /* Are we supposed to run this one through a function? */
	  if(vars[n].Type == RC_PARSE_FUNC)
	  {
	       /* Use the outside function specified in the structure
		* to parse these line elements since their grammar is 
		* somewhat weird 
		*/
	       if(!vars[n].func(varPrefix, varName, varVal, vars[n].Val))
	       {
		    fprintf(stderr,"There was an error parsing \"%s\"\n",
			    Line); fflush(stderr);
	       } /* End if */

	       continue;  /* Done with this line */
	  } /* End if */
	      
	  /* We're not supposed to run this through a function --
	   * based on the variable's type, set the C variable to its saved
	   * value
	   */
	  switch(vars[n].Type)
	  {
	  case (RC_INT):
	       if(g_strcasecmp(varVal,"ON")   == 0 ||
		  g_strcasecmp(varVal,"SHOW") == 0)
		    *(gint *)(vars[n].Val) = ON;
	       else 
		    *(gint *)(vars[n].Val) = atoi(varVal);
	       break;
	  case (RC_BOOL):
	       if(g_strcasecmp(varVal,"ON")   == 0 ||
		  g_strcasecmp(varVal,"SHOW") == 0)
		    abool = ON;
	       else
		    abool = atoi(varVal);
	       if(abool)
		    *(gint *)(vars[n].Val) = ON;
	       else *(gint *)(vars[n].Val) = OFF;
	       break;
	  case (RC_STR):
	  {
	       char *tok;
	       
	       /* varVal is not trustworthy, find the string 
		* within the quotes and use that instead. 
		*/
	       if(strstr(Line, "\""))
	       {
		    tok = strtok(Line,"\"");
		    if(!tok)
		    {
			 /* This really shouldn't happen */
                         if(complain)
                         {
                              fprintf(stderr,"Parse error within \"%s\"\n",
                                      Line);
                              fflush(stderr);
                         } /* End if */
			 break;
		    } /* End if */
		    tok = strtok(NULL,"\"");
	       } /* End if */
	       else 
		    tok = &varVal[0];

	       /* free the current contents of the variable */
	       if( *(gchar **)(vars[n].Val) )
		    g_free_( *(gchar **)vars[n].Val);
	       
	       /* set the variable to its new value. */
	       if(tok)
	       {
		    *(gchar**)(vars[n].Val) = g_strdup_(tok);
	       } /* End if */
	       else *(gchar**)(vars[n].Val) = (char *)NULL;
	       break;
	  } /* End block */
	  } /* End switch */
     } /* End while */
     
     /* clean up and exit */
     g_hash_table_destroy(H);
     fclose(rcFile);
     return 0;
} /* End read_ConfigFile() */

int write_ConfigFile(char *filename, RCVARS *vars)                           
{
     int n=0;
     FILE *rcFile;
     
     if((rcFile= fopen(filename,"w")) == NULL)
     {
	  fprintf(Q,"write_ConfigFile Error:  Couldn't write to %s:  %s\n",
		  filename, g_strerror(errno));
	  fflush(Q);
	  return(-1);
     } /* End if */
     
     /* warn the user not to screw around with the file... */
     fputs("# Configure these settings the through application-do not edit!\n",
	   rcFile);
     
     n = 0;
     /* go through all the persistent variables, up to the end-marker */
     while(vars[n].Type != RC_NONE)
     {
	  /* based on its type, print out a line for the variable. */
	  switch(vars[n].Type)
	  {
	  case (RC_INT):
	       if(vars[n].Prefix)
		    fprintf(rcFile," %s %s %d\n",
			    vars[n].Prefix,vars[n].Name, *(gint *)vars[n].Val);
	       else
		    fprintf(rcFile," %s = %d\n",
			    vars[n].Name,*(gint *)vars[n].Val);
	       break;
	  case (RC_BOOL):
	       if(vars[n].Prefix)
		    fprintf(rcFile," %s %s %d\n",
			    vars[n].Prefix,vars[n].Name, 
			    (*(gint *)vars[n].Val ? 1 : 0));
	       else
		    fprintf(rcFile," %s = %d\n",
			    vars[n].Name, (*(gint *)vars[n].Val ? 1 : 0));
	       break;
	  case (RC_STR):
	       if(vars[n].Prefix && *(gchar **)(vars[n].Val))
	       {
		    fprintf(rcFile," %s %s \"%s\"\n",
			    vars[n].Prefix, vars[n].Name, 
			    *(char **)(vars[n].Val));
	       } /* End if */
	       else
	       {
		    if( *(gchar **)(vars[n].Val) )
                    {
			 fprintf(rcFile," %s = \"%s\"\n",
				 vars[n].Name,*(char **)(vars[n].Val));
                    }
	       } /* End else */

	       break;
	  } /* End switch */

	  n++; /* next entry */
     } /* End while */
     
     /* clean up and exit */
     fclose(rcFile);
     return 0;
} /* End write_ConfigFile() */
