option.lib.c
/****************************************************************************************************/
/*                                                                                                  */
/* option.lib.c:                                                                                    */
/*                                                                                                  */
/****************************************************************************************************/

/****************************************************************************************************/
/*                                                                                                  */
/*     Copyright (C) 2004 Joerg Kunze                                                               */
/*                                                                                                  */
/*     This file is part of siliconBrain.                                                           */
/*                                                                                                  */
/*     siliconBrain 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.                                                          */
/*                                                                                                  */
/*     siliconBrain 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 General Public License                            */
/*     along with this program; if not, write to the Free Software                                  */
/*     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                    */
/*                                                                                                  */
/****************************************************************************************************/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#include <pwd.h>

#include "siliconBrainLib"
#include "siliconBrainSpecification"
#include "siliconBrainOption"

// static const char *siliconBrainRelease       = "$siliconBrainRelease: 0.2.3 $";
// static const char *siliconBrainRcsIdentifier = "$Id: option.lib.c,v 1.8 2004/12/14 23:31:26 joerg Exp $";
// static const char *siliconBrainSaveStamp     = "$siliconBrainSaveStamp: 2004/10/29 23:40:58, Joerg Kunze$";

/****************************************************************************************************/
/*                                                                                                  */
/* typedef:                                                                                         */
/*                                                                                                  */
/****************************************************************************************************/
typedef enum {
   localContextNo,
   localContextInPackage,
   localContextInOption,
   localContextOptionUnknown,
} LocalXmlContextState;

typedef struct {
   OptionProcessed      *options;
   OptionProcessed      *currentOption;
   LocalXmlContextState  state;
   Obstack              *obstack;
   GHashTable           *optionNames;
} Local;

/****************************************************************************************************/
/*                                                                                                  */
/* parser:                                                                                          */
/*                                                                                                  */
/****************************************************************************************************/
static void xmlStartElement(
   GMarkupParseContext *context,
   const gchar         *name,
   const gchar        **attribute,
   const gchar        **value,
   gpointer             userData,
   GError             **error
) {
   Local *user = userData;

   switch( user->state ) {
   case localContextNo:
      user->state = localContextInPackage;
      break;

   case localContextInPackage:
      user->currentOption = g_hash_table_lookup( user->optionNames, name );
      user->state = user->currentOption ? localContextInOption : localContextOptionUnknown;
      break;

   case localContextInOption:
   case localContextOptionUnknown:
      break;
   }
}

static void xmlText(
   GMarkupParseContext *context,
   const gchar         *text,
   gsize                textLength,
   gpointer             user_data,
   GError             **error
) {
   Local *user = user_data;

   /*---------------------------------*/
   /* ignore text in ceratin contexts */
   /*---------------------------------*/
   switch( user->state ) {
   case localContextNo:
   case localContextInPackage:
   case localContextOptionUnknown:
      return;
   default: break;
   }

   if( user->currentOption->alreadyConfigured ) return;

   Obstack    *obstack = user->obstack;
   const char *textEnd = text + textLength - 1;
   char       *textSavedAsString;

   /*--------------------------------------------*/
   /* trim text: skip left and right white space */
   /*--------------------------------------------*/
   while( textLength ) {
      switch( *text ) {
      case ' ': case '\n': case '\t':
         --textLength;
         ++text;
         break;
      default:
         goto endOfPrefix;
      }
   }
 endOfPrefix:

   while( textLength ) {
      switch( *textEnd ) {
      case ' ': case '\n': case '\t': case 0:
         --textLength;
         --textEnd;
         break;
      default:
         goto endOfSuffix;
      }
   }
 endOfSuffix:

   textSavedAsString = obstack_copy0( obstack, text, textLength );

   switch( user->currentOption->optionType ) {

   case optionValue:
      *((char **)user->currentOption->value) = textSavedAsString;
      break;

   case optionFlag:
      *((bool *)user->currentOption->value) = stringToBool( textSavedAsString );
      break;
   }

   user->currentOption->alreadyConfigured = true;
}

static void xmlEndElement(
   GMarkupParseContext *context,
   const gchar         *element_name,
   gpointer             user_data,
   GError             **error
) {
   Local *user = user_data;

   switch( user->state ) {
   case localContextNo:
      user->state = localContextNo;
      break;
   case localContextInPackage:
      user->state = localContextNo;
      break;
   case localContextInOption:
      if( !user->currentOption->alreadyConfigured ) xmlText( context, "", 0, user_data, error );
   case localContextOptionUnknown:
      user->state = localContextInPackage;
      break;
   }
}

static GMarkupParser parser = { &xmlStartElement, &xmlEndElement, &xmlText, 0, 0 };

/****************************************************************************************************/
/*                                                                                                  */
/*                                                                                                  */
/*                                                                                                  */
/****************************************************************************************************/
void optionReadOptions( OptionProcessed options[], Obstack *obstack, const char * const packageName ) {

   char    xmlStreamChunk[ 1234 ];
   size_t  xmlStreamChunkLength;
   GError *error;

   Local local = {
      .options       = options,
      .currentOption = 0,
      .state         = localContextNo,
      .obstack       = obstack,
      .optionNames   = 0
   };

   local.optionNames = g_hash_table_new( g_str_hash, g_str_equal );

   OptionProcessed *option;

   for( option = local.options; option->name; ++option )
      g_hash_table_insert( local.optionNames, (char *)option->name, option );

   bool *complete = ((OptionProcessed *)g_hash_table_lookup( local.optionNames, "complete" ))->value;

   GMarkupParseContext *context = g_markup_parse_context_new( &parser, 0, &local, 0 );

   FILE *xmlSource = 0;

   #define defaultNumberOfConfigurationDirectories 42

   char *configurationDirectoriesImplementation[ defaultNumberOfConfigurationDirectories ] = {
      "./"   ,
      "~/"   ,
      "/etc/",
      ""     ,
      0
   };

   char **configurationDirectories = configurationDirectoriesImplementation;

   char **configurationDirectory;

   char *configurationPath = getenv( "siliconBrainConfigurationPath" );
   char *configurationPathIterator;

   if( configurationPath ) {

      int numberOfDirectoryEntries = 1;

      for( configurationPathIterator = configurationPath; *configurationPathIterator; ++configurationPathIterator ) {
         if( *configurationPathIterator == ':' ) ++numberOfDirectoryEntries;
      }

      if( numberOfDirectoryEntries <= defaultNumberOfConfigurationDirectories ) {
         configurationDirectories = obstack_alloc( obstack, numberOfDirectoryEntries * sizeof( *configurationDirectory ) + 1 );
      }

      configurationPathIterator = configurationPath;
      configurationDirectory    = configurationDirectories;

      char *begin;

      int length;

      while( true ) {

         while( *configurationPathIterator == ' ' || *configurationPathIterator == '\t' ) ++configurationPathIterator;

         begin = configurationPathIterator;

         length = 0;
         while( *configurationPathIterator != ':' && *configurationPathIterator != 0 ) ++length, ++configurationPathIterator;

         while( *(begin+length-1) == ' ' || *(begin+length-1) == '\t' ) --length;

         if( *(begin+length-1) != '/' ) ++length;

         *configurationDirectory = obstack_copy0( obstack, begin, length );

         if( *(*configurationDirectory+length-1) != '/' ) *(*configurationDirectory+length-1) = '/';

         ++configurationDirectory; *configurationDirectory = 0;

         if( *configurationPathIterator == 0 ) break;
         else ++configurationPathIterator;
      }

      *configurationDirectory = 0;
   }

   char  *fileName;

   int saveStderr = dup( STDERR_FILENO );
   freopen( "/dev/null", "w", stderr );

   for( configurationDirectory = configurationDirectories; !*complete && *configurationDirectory; ++configurationDirectory ) {

      osprintf( obstack, &fileName, "%s%s.configuration", *configurationDirectory, packageName );

      if( !(xmlSource = popen( fileName, "r" ) ) )
         continue;

      while( xmlStreamChunkLength = fread( xmlStreamChunk, 1, sizeof( xmlStreamChunk ) - 1, xmlSource ) )
         g_markup_parse_context_parse( context, xmlStreamChunk, xmlStreamChunkLength, &error);
   }

   fclose( stderr );
   stderr = fdopen( saveStderr, "w+" );

   g_markup_parse_context_end_parse( context, &error );
   g_markup_parse_context_free     ( context         );

   g_hash_table_destroy( local.optionNames );
}