Logo Search packages:      
Sourcecode: afterstep version File versions

parser.c

/*
 * Copyright (c) 2000 Andrew Ferguson <andrew@owsla.cjb.net>
 * Copyright (c) 1998 Sasha Vasko <sasha at aftercode.net>
 *
 * 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 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.
 *
 */

#undef LOCAL_DEBUG
#undef DO_CLOCKING
#undef UNKNOWN_KEYWORD_WARNING

#include "../configure.h"

#include <fcntl.h>
#include <unistd.h>

#ifdef DO_CLOCKING
#if TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# if HAVE_SYS_TIME_H
#  include <sys/time.h>
# else
#  include <time.h>
# endif
#endif
#endif
#include "asapp.h"
#include "afterstep.h"
#include "parser.h"
#include "freestor.h"
#include "functions.h"

char         *_disabled_keyword = DISABLED_KEYWORD;
char         *_unknown_keyword = "unknown";

static ASHashTable *Keyword2IDHash = NULL ; 


void 
register_keyword_id( const char *keyword, int id ) 
{
      ASHashData hdata = {0};

      if( Keyword2IDHash == NULL )
            Keyword2IDHash = create_ashash( 0, NULL, NULL, NULL );

      hdata.cptr = (char*)keyword ; 
      add_hash_item( Keyword2IDHash, AS_HASHABLE(id), hdata.vptr );
}        

const char*
keyword_id2keyword( int id )  
{
      ASHashData hdata = {0};

      if( Keyword2IDHash == NULL )  
            return _unknown_keyword;
      
      if( get_hash_item(Keyword2IDHash, AS_HASHABLE(id), &hdata.vptr) == ASH_Success ) 
            return hdata.cptr;
      
      return _unknown_keyword;
}

void
BuildHash (SyntaxDef * syntax)
{
      register int  i;

      if (syntax->term_hash_size <= 0)
            syntax->term_hash_size = TERM_HASH_SIZE;
      if (syntax->term_hash == NULL)
            syntax->term_hash = create_ashash (syntax->term_hash_size, option_hash_value, option_compare, NULL);
      for (i = 0; syntax->terms[i].keyword; i++)
      {     
            add_hash_item (syntax->term_hash, AS_HASHABLE(syntax->terms[i].keyword), (void *)&(syntax->terms[i]));
            register_keyword_id( syntax->terms[i].keyword, syntax->terms[i].id );
      }
}

TermDef      *
FindStatementTerm (char *tline, SyntaxDef * syntax)
{
    ASHashData hdata = {0};
    LOCAL_DEBUG_OUT( "looking for pterm in hash table  %p of the syntax %s ", syntax->term_hash, syntax->display_name );
    if (get_hash_item (syntax->term_hash, AS_HASHABLE(tline), &hdata.vptr)!=ASH_Success  )
            hdata.vptr = NULL;
    LOCAL_DEBUG_OUT( "FOUND pterm %p", hdata.vptr );
    return hdata.vptr;
}

void
PrepareSyntax (SyntaxDef * syntax)
{
      if (syntax)
      {
            register int  i;

            if (syntax->recursion > 0)
                  return;
            syntax->recursion++;

            BuildHash (syntax);
            for (i = 0; syntax->terms[i].keyword; i++)
                  if (syntax->terms[i].sub_syntax)
                        /* this should prevent us from endless recursion */
                        if (syntax->terms[i].sub_syntax->term_hash == NULL)
                              PrepareSyntax (syntax->terms[i].sub_syntax);

            syntax->recursion--;
      }
}

void
FreeSyntaxHash (SyntaxDef * syntax)
{
      if (syntax)
      {
            register int  i;

            if (syntax->recursion > 0)
                  return;
            syntax->recursion++;

            if (syntax->term_hash)
                  destroy_ashash (&(syntax->term_hash));

            for (i = 0; syntax->terms[i].keyword; i++)
                  if (syntax->terms[i].sub_syntax)
                        /* this should prevent us from endless recursion */
                        if (syntax->terms[i].sub_syntax->term_hash != NULL)
                              FreeSyntaxHash (syntax->terms[i].sub_syntax);

            syntax->recursion--;

      }
}

/* Syntax and storage stack operations */
void
PushSyntax (ConfigDef * config, SyntaxDef * syntax)
{
      SyntaxStack  *pnew = (SyntaxStack *) safemalloc (sizeof (SyntaxStack));

      /* saving our status to be able to come back to it later */
      if (config->current_syntax)
      {
            config->current_syntax->current_term = config->current_term;
            config->current_syntax->current_flags = config->current_flags;
      }
      pnew->next = config->current_syntax;
      pnew->syntax = syntax;
      if (config->syntax && syntax->terminator == '\n')
      {                                                              /* handling prepending */
            SyntaxDef    *csyntax = config->syntax;
            int           len = strlen (csyntax->prepend_sub);
            register int  i;
            char         *tmp;

            if (config->current_prepend_allocated - config->current_prepend_size <= len)
            {
                  config->current_prepend_allocated += len + 1;
                  tmp = safemalloc (config->current_prepend_allocated);
                  if (config->current_prepend)
                  {
                        strcpy (tmp, config->current_prepend);
                        free (config->current_prepend);
                  }
                  config->current_prepend = tmp;
            }
            tmp = &(config->current_prepend[config->current_prepend_size]);

            for (i = 0; i <= len; i++)
                  tmp[i] = csyntax->prepend_sub[i];

            config->current_prepend_size += len;

      }
#ifdef DEBUG_PARSER
      fprintf (stderr, "PushSyntax(%s, old is 0x%lX) current_prepend = [%s]\n",
                   syntax->display_name, (unsigned long)config->syntax, config->current_prepend);
#endif
      config->current_syntax = pnew;
      config->syntax = syntax;
}

int
PopSyntax (ConfigDef * config)
{
      if (config->current_syntax->next)
      {
            SyntaxStack  *pold = config->current_syntax;

            config->current_syntax = config->current_syntax->next;
            config->syntax = config->current_syntax->syntax;

            /* restoring our status */
            config->current_term = config->current_syntax->current_term;
            config->current_flags = config->current_syntax->current_flags;
#ifdef DEBUG_PARSER
            if (config->current_term)
                  fprintf (stderr,
                         "PopSyntax(%s, flags = 0x%lX, term = [%s] ; old is 0x%lX)\n",
                         config->syntax->display_name, config->current_flags,
                         config->current_term->keyword, (unsigned long)(pold->syntax));
            else
                  fprintf (stderr,
                         "PopSyntax(%s, flags = 0x%lX, term = [%s] ; old is 0x%lX)\n",
                         config->syntax->display_name, config->current_flags,
                         "", (unsigned long)(pold->syntax));
#endif
            if (pold->syntax->terminator == '\n')
            {
                  if ((config->current_prepend_size -= strlen (config->syntax->prepend_sub)) >= 0)
                        config->current_prepend[config->current_prepend_size] = '\0';
            }
/*fprintf( stderr, "PopSyntax(%s) current_prepend = [%s]\n", pold->syntax->display_name, config->current_prepend );
*/
            free (pold);
            return 1;
      }
      return 0;
}

void
PushStorage (ConfigDef * config, FreeStorageElem ** tail)
{
      StorageStack *pnew = (StorageStack *) safemalloc (sizeof (StorageStack));

      pnew->tail = tail;
      pnew->next = config->current_tail;
      config->current_tail = pnew;
}

int
PopStorage (ConfigDef * config)
{
      if (config->current_tail)
            if (config->current_tail->next)
            {
                  StorageStack *pold = config->current_tail;

                  config->current_tail = config->current_tail->next;
                  free (pold);
                  return 1;
            }
      return 0;
}


void
config_error (ConfigDef * config, char *error)
{
      if (config)
      {
            char         *eol = strchr (config->tline, '\n');

            if (eol)
                  *eol = '\0';
            show_error ("in %s (line %d):%s[%.50s]",
                            config->current_syntax->syntax->display_name, config->line_count, error, config->tline);
            if (eol)
                  *eol = '\n';
      }
}

/* Creating and Initializing new ConfigDef */
ConfigDef    *
NewConfig (char *myname, SyntaxDef * syntax, ConfigDataType type, ConfigData  source, SpecialFunc special, int create)
{
      ConfigDef    *new_conf;

      if (myname == NULL)
            return NULL;

    new_conf = (ConfigDef *) safecalloc (1, sizeof (ConfigDef));
      new_conf->special = special;
      new_conf->fd = -1;
      new_conf->fp = NULL;
    new_conf->flags = 0;
      if (source.vptr != NULL )
            switch (type)
            {
             case CDT_Filename:
                   {
                         char         *realfilename = put_file_home (source.filename);

                         if (!realfilename)
                         {
                               free (new_conf);
                               return NULL;
                         }
                         new_conf->fd =
#ifdef __CYGWIN__
                     open (realfilename, create ? O_CREAT | O_RDONLY |O_BINARY: O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP|O_BINARY);
#else
                     open (realfilename, create ? O_CREAT | O_RDONLY: O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP);
#endif
                         free (realfilename);
                 set_flags( new_conf->flags, CP_NeedToCloseFile);
                   }
                   break;
             case CDT_FilePtr:
                   new_conf->fp = source.fileptr;
                   new_conf->fd = fileno (new_conf->fp);
                   break;
             case CDT_FileDesc:
                   new_conf->fd = *source.filedesc;
                   break;
             case CDT_Data:
                   break;
         case CDT_FilePtrAndData :
             new_conf->fp = source.fileptranddata->fp;
                   new_conf->fd = fileno (new_conf->fp);
             set_flags( new_conf->flags, CP_ReadLines );
            break ;

            }

      if (new_conf->fd != -1 && new_conf->fp == NULL)
        new_conf->fp = fdopen (new_conf->fd, get_flags(new_conf->flags, CP_ReadLines)?"rt":"rb");

      new_conf->myname = mystrdup(myname);
      new_conf->current_syntax = NULL;
      new_conf->current_tail = NULL;
      new_conf->current_prepend = NULL;
      new_conf->current_prepend_size = 0;
      new_conf->current_prepend_allocated = 0;

      PushSyntax (new_conf, syntax);

      PrepareSyntax (syntax);

      /* allocated to store lines read from the file */
      new_conf->buffer = NULL;
      new_conf->buffer_size = 0;
      new_conf->bytes_in = 0;

      /* this is the current parsing information */
      new_conf->tline = new_conf->tdata = new_conf->tline_start = NULL;
      new_conf->current_term = NULL;
      new_conf->current_data_size = MAXLINELENGTH + 1;
      new_conf->current_data = (char *)safemalloc (new_conf->current_data_size);
      new_conf->current_data_len = 0;
      new_conf->cursor = NULL;
      return new_conf;
}

/* reader initialization */
ConfigDef    *
InitConfigReader (char *myname, SyntaxDef * syntax, ConfigDataType type, ConfigData source, SpecialFunc special)
{
      ConfigDef    *new_conf = NewConfig (myname, syntax, type, source, special, False);

      if (new_conf == NULL)
            return NULL;
      if (source.vptr == NULL)
      {
            DestroyConfig (new_conf);
            return NULL;
      }

      if (type == CDT_Data)
      {
            /* allocate to store entire data */
            new_conf->buffer_size = strlen (source.data) + 1;
            new_conf->buffer = (char *)safemalloc (new_conf->buffer_size);
            strcpy (new_conf->buffer, source.data);
            new_conf->bytes_in = new_conf->buffer_size - 1;
    } else if( type == CDT_FilePtrAndData )
    {
        FilePtrAndData *fpd = source.fileptranddata;
        int buf_len = fpd->data?strlen( fpd->data ):0;
        if( buf_len < MAXLINELENGTH )
            buf_len = MAXLINELENGTH ;
        new_conf->buffer_size = buf_len ;
        new_conf->buffer = (char *)safemalloc (buf_len + 1);
        if( fpd->data )
            strcpy (new_conf->buffer, fpd->data);
        else
            new_conf->buffer[0] = '\0';
    }else
      {
            new_conf->buffer_size = MAXLINELENGTH + 1;
            new_conf->buffer = (char *)safemalloc (new_conf->buffer_size);
            new_conf->buffer[0] = '\0';
      }

      /* this is the current parsing information */
      new_conf->cursor = &(new_conf->buffer[0]);
      new_conf->line_count = 1;

      return new_conf;
}

/* debugging stuff */
#ifdef DEBUG_PARSER
void
PrintSyntax (SyntaxDef * syntax)
{
      if (syntax->recursion > 0)
            return;
      syntax->recursion++;

      fprintf (stderr, "\nSentence Terminator: [0x%2.2x]", syntax->terminator);
      fprintf (stderr, "\nConfig Terminator:   [0x%2.2x]", syntax->file_terminator);
      syntax->recursion--;
}


void
PrintConfigReader (ConfigDef * config)
{
      PrintSyntax (config->syntax);
}

#endif


/* reader de-initialization */
void
DestroyConfig (ConfigDef * config)
{
      free (config->myname);
      if (config->buffer)
            free (config->buffer);
      if (config->current_data)
            free (config->current_data);
      while (PopSyntax (config));
      if (config->current_syntax);
      free (config->current_syntax);
      while (PopStorage (config));
      if (config->current_prepend)
            free (config->current_prepend);
      if (config->current_tail)
            free (config->current_tail);
      if (config->syntax)
            FreeSyntaxHash (config->syntax);
    if (get_flags(config->flags, CP_NeedToCloseFile) && config->fd != -1)
            close (config->fd);
      free (config);
}

void print_trimmed_str( char *prompt, char * str )
{
#ifdef LOCAL_DEBUG
    int i = 0;
    char tmp ;

    while( str[i] && i < 20 )
    {
        if( str[i] == '\n' )
            str[i] = '}';
        ++i ;
    }
    tmp = str[i] ;
    str[i] = '\0';
    LOCAL_DEBUG_OUT( "first %d chars of %s:\"%s\"", i, prompt, str );
    str[i] = tmp;
    while( --i >= 0 )
        if( str[i] == '}' )
            str[i] = '\n';
#endif
}

char         *
GetToNextLine (ConfigDef * config)
{
      register char terminator = config->syntax->terminator;
      register char file_terminator = config->syntax->file_terminator;
      register char *cur = config->cursor, *buffer_end = &(config->buffer[config->bytes_in]);

      for (; *cur != '\0' && cur < buffer_end; cur++)
      {
            if (*cur == '\n')
                  config->line_count++;
            if (*cur == terminator)
                  break;
            if (*cur == file_terminator)
                  break;
      }
      if (cur < buffer_end && *cur != '\0')
            cur++;

      config->cursor = cur;

      if (cur >= buffer_end)
      {
        if (config->fp)
            {
            if( get_flags( config->flags, CP_ReadLines ) )
            {
LOCAL_DEBUG_OUT( "Reading Lines ...%s", "" );
                if (!fgets (config->buffer, config->buffer_size, config->fp))
                    return NULL;
                config->bytes_in = strlen(config->buffer);
                config->cursor = &(config->buffer[0]);
            }else
            {
                register int  i;
                register char *ptr = config->buffer ;

LOCAL_DEBUG_OUT( "Reading Buffer ...%s", "" );
                config->bytes_in = read (config->fd, ptr, config->buffer_size);
                if (config->bytes_in <= 0)
                    return NULL;
                print_trimmed_str( "new data begins with", ptr );
                /* now we want to get back to the last end-of-line
                so not to break statements in half */
                for (i = config->bytes_in - 1; i >= 0; i--)
                    if (ptr[i] == '\n')
                        break;
                i++;
                if (i > 0)
                {
                    lseek (config->fd, i - (config->bytes_in), SEEK_CUR);
                    config->bytes_in = i;
                }
                ptr[config->bytes_in] = '\0';
#ifdef __CYGWIN__                              /* fuck Microsoft !!!!! */
                while( --i >= 0 )
                    if( ptr[i] == 0x0D )
                        ptr[i] = 0x0A;
#endif
            }
            config->cursor = &(config->buffer[0]);
        } else
                  return NULL;
      } else if (*cur == file_terminator)
            return NULL;

      return (config->cursor);
}

/* this function finds next valid statement:
   - not comments,
   - not an empty line,
   - if prepended with * - should have MyName at the beginning
   and :
   - sets tline to the beginning of the keyword (not MyName),
   - copies arguments into the current_data
   - sets current_data_len
   it will read data from the file as needed untill the end of file
   or config->syntax.file_terminator or EOF is reached
   Return: NULL if end of config reached, otherwise same as tline.
 */
char         *
GetNextStatement (ConfigDef * config, int my_only)
{
      char         *cur = config->cursor;          /* don't forget to save it back ! */
      register char *data;
      char          terminator = config->syntax->terminator;
      char          file_terminator = config->syntax->file_terminator;

      while (1)
      {
            config->tline_start = cur;                   /* remember beginning of the entire config option */
            for (; *cur != '\0' && *cur != terminator; cur++)
            {
                  if (*cur == file_terminator)
                        return NULL;
#ifdef __CYGWIN__
            if (!isspace ((int)*cur) && *cur != 0x0D)
#else
            if (!isspace ((int)*cur))
#endif
                  {
                        register int  i;

                        config->current_flags = CF_NONE;

                        if (*cur == COMMENTS_CHAR)
                        {                                            /* let's check for DISABLE keyword */
                    print_trimmed_str( "comments at", cur );
                              for (i = 1; i < DISABLED_KEYWORD_SIZE; i++)
                                    if (*(cur + i) == '\0' || *(cur + i) != _disabled_keyword[i])
                                          break;
                              if (i < DISABLED_KEYWORD_SIZE)
                              {                                      /* comments - skip entire line */
                                    while (*cur != '\n' && *cur != '\0')
                                          cur++;
                                    config->cursor = cur;
                                    break;
                              }
                              config->current_flags |= CF_DISABLED_OPTION;
                              /* let's skip few spaces here */
                              cur = cur + i;
                              while (isspace ((int)*cur) && *cur != terminator)
                                    cur++;
                              if (*cur == '\0' || *cur == terminator)
                                    break;                           /* not a valid option */
                        }

                        if (*cur == MYNAME_CHAR)
                        {                                            /* check if we have MyName here */
                              register char *mname = config->myname;

                    print_trimmed_str(  "private option at", cur );
                              while (*cur != '\0' && *mname != '\0')
                              {
                                    if (tolower (*(mname++)) != tolower (*(++cur)))
                                          break;
                              }
                              if (*mname != '\0')
                              {                                      /* that was a foreign optiion - belongs to the other executable */
                                    if (my_only)
                                          break;
                                    config->current_flags |= CF_FOREIGN_OPTION;
                              }
                              cur++;
                        } else
                {

                    if( *cur == terminator || *cur == file_terminator )   /* public option keyword may not be empty ! */
                        break;
                    config->current_flags |= CF_PUBLIC_OPTION;
                    print_trimmed_str( "public option at", cur );
                }
                        config->tline = cur;             /*that will be the begginnig of the term */

                        /* now we should copy everything from after the first space to
                           config->current_data and set current_data_len ; */
                i = 0 ;
                while (cur[i] && !isspace ((int)cur[i]) && cur[i] != terminator && cur[i] != file_terminator)
                    ++i;
                while ((*cur) && isspace ((int)cur[i]) && cur[i] != terminator && cur[i] != file_terminator)
                    ++i;
                cur = &(cur[i]);           /* that will be the beginning of our data */
                config->tdata = cur;
                print_trimmed_str( "data start at :", cur );
                        data = config->current_data;
                for (i = 0; cur[i] && cur[i] != terminator && cur[i] != file_terminator; ++i)
                        {
                              /* buffer overrun prevention */
                              if (i >= config->current_data_size)
                              {
                                    config->current_data_size += MAXLINELENGTH >> 3;
                                    config->current_data = (char *)realloc (config->current_data, config->current_data_size);
                                    if (config->current_data == NULL)
                                    {
                                          config_error (config, "Not enough memory to hold option's arguments");
                                          exit (0);
                                    }
                        data = config->current_data;
                              }
                    data[i] = cur[i];
                        }
                LOCAL_DEBUG_OUT( "%d bytes of data stored", i );
                cur = &(cur[i]);
                        /* now let's go back and remove trailing spaces */
                        if (config->tdata[0] == file_terminator)
                              config->current_flags |= CF_LAST_OPTION;
                        else
                        {
                    for (i--; i >= 0; i--)
                              {
                                    if (config->tdata[i] == file_terminator)
                                          config->current_flags |= CF_LAST_OPTION;
                                    if (!isspace ((int)data[i]))
                                          break;
                              }
                              i++;
                        }
                        /* since \0 is our normal data terminator we really should trigger
                           end of the configuration when the file ends : */
                        if (file_terminator == '\0')
                              config->current_flags &= ~CF_LAST_OPTION;
                LOCAL_DEBUG_OUT( "%d bytes of clean data stored", i );
                        config->current_data_len = i;
                        config->current_data[i] = '\0';

                        if (*cur && *cur == terminator)
                              cur++;
                        config->cursor = cur;            /* Saving position for future use */
                        return config->tline;
                  }
            }
            /* reading file here */
            if ((cur = GetToNextLine (config)) == NULL)
                  return NULL;
      }
      return NULL;
}



void
ProcessSubSyntax (ConfigDef * config, FreeStorageElem ** tail, SyntaxDef * syntax)
{
      register char *ptr;

      PushStorage (config, tail);
      if (config->syntax->terminator == syntax->file_terminator)
      {                                                              /* need to push back term's data into config buffer */
            config->cursor = config->tdata;
            if (config->current_term->flags & TF_NAMED_SUBCONFIG)
                  /* we are supposed to skip single quoted text in here, or unquoted token */
            {
                  if (*(ptr = config->cursor) == '"')
                  {
                        ptr = find_doublequotes (ptr);
                        if (!ptr)
                        {
                              config_error (config, "Unmatched doublequotes detected\n");
                              ptr = config->cursor;
                        } else
                              ptr++;                                 /* skipping current doubleqoute */
                  } else                                             /*simply skipping to the next space */
                        for (; *ptr && !isspace ((int)*ptr); ptr++);
                  config->cursor = ptr;
            }
      } else if (config->syntax->terminator == syntax->terminator)
      {                                                              /* need to push back entire term's line into config buffer */
            config->cursor = config->tline_start;
      }

/*  fprintf( stderr, "\nprocessing as subsyntax: [%s]", config->cursor );
*/
      PushSyntax (config, syntax);
}

void
ProcessStatement (ConfigDef * config)
{
      FreeStorageElem *pNext;
      TermDef      *pterm = config->current_term;
LOCAL_DEBUG_OUT( "checking for foreign option ...%s", "" );
      if (IsForeignOption (config))
            return;

LOCAL_DEBUG_OUT( "adding storage ...%s", "" );
      if ((pNext = AddFreeStorageElem (config->syntax, config->current_tail->tail, pterm, ID_ANY)) == NULL)
            return;

LOCAL_DEBUG_OUT( "parsing stuff ...%s", "" );
      pNext->flags = config->current_flags;

      if (config->current_data_len > 0 && !(pterm->flags & TF_DONT_REMOVE_COMMENTS))
      {
            stripcomments (config->current_data);
            config->current_data_len = strlen (config->current_data);
      }
    print_trimmed_str( "config->current_data", config->current_data );
    LOCAL_DEBUG_OUT( "curr_data_len = %d", config->current_data_len);

      args2FreeStorage (pNext, config->current_data, config->current_data_len);

      config->current_tail->tail = &(pNext->next);
      if (pterm->sub_syntax)
            ProcessSubSyntax (config, &(pNext->sub), pterm->sub_syntax);
      return;
}



/* main parsing procedure */
int
ParseConfig (ConfigDef * config, FreeStorageElem ** tail)
{
      int           TopLevel = 0;
      unsigned long flags;

      PushStorage (config, tail);
      /* get line */
      while (!TopLevel)
      {
            while (GetNextStatement (config, 1))
            {                                                        /* untill not end of text */
                  flags = 0x00;
#ifdef DEBUG_PARSER
                  fprintf (stderr, "\nSentence Found:[%.50s ...]\n,\tData=\t[%s]", config->tline, config->current_data);
                  fprintf (stderr, "\nLooking for the Term...");
#endif
                  /* find term */
                  if ((config->current_term = FindStatementTerm (config->tline, config->syntax)))
                  {
#ifdef DEBUG_PARSER
                        fprintf (stderr, "\nTerm Found:[%s]", config->current_term->keyword);
#endif
                        if (get_flags( config->current_term->flags, TF_OBSOLETE))
                              config_error (config, "Heh, It seems that I've encountered obsolete config option. I'll ignore it for now, Ok ?!");
                        if (get_flags( config->current_term->flags, TF_PHONY))
                              set_flags( config->flags, CF_PHONY_OPTION );
                        if (get_flags( config->current_term->flags, TF_SPECIAL_PROCESSING))
                        {
                              if (config->special)
                              {
                                    FreeStorageElem **ctail = config->current_tail->tail;

                                    flags = (*(config->special)) (config, ctail);
                                    if (get_flags (flags, SPECIAL_BREAK))
                                          break;
                                    if (get_flags (flags, SPECIAL_STORAGE_ADDED))
                                    {
                                          for (ctail = config->current_tail->tail; (*ctail); ctail = &((*ctail)->next));
                                          tail = config->current_tail->tail = ctail;
                                    }
                              }
                        }
                        if (!get_flags (flags, SPECIAL_SKIP))
                              ProcessStatement (config);

                        if ((config->current_term->flags & TF_SYNTAX_TERMINATOR) || IsLastOption (config))
                              break;
                  } else
                  {
#ifdef UNKNOWN_KEYWORD_WARNING
                        config_error (config, " unknown keyword encountered");
#endif
                if (IsLastOption (config))
                              break;
                  }
            }                                                        /* end while( GetNextStatement() ) */
            /* trying to see if we can get to higher level syntax */
            if (!PopSyntax (config))
                  TopLevel = 1;
            if (!PopStorage (config))
                  TopLevel = 1;
            while ((TopLevel != 1) &&
                     ((config->current_term &&
                         (config->current_term->flags & TF_SYNTAX_TERMINATOR)) || IsLastOption (config)))
            {
                  if (!PopSyntax (config))
                        TopLevel = 1;
                  if (!PopStorage (config))
                        TopLevel = 1;
            }
      }                                                              /* end while( !TopLevel ) */

      return 1;
}


FreeStorageElem *
file2free_storage(const char *filename, char *myname, SyntaxDef *syntax, SpecialFunc special, FreeStorageElem **foreign_options )
{
      ConfigData cd ;
      ConfigDef    *config_reader ;
      FreeStorageElem *storage = NULL ;
      
      cd.filename = filename ;
      config_reader = InitConfigReader (myname, syntax, CDT_Filename, cd, special);
      if (!config_reader)
            return NULL;

      PrintConfigReader (config_reader);
      ParseConfig(config_reader, &storage);
      PrintFreeStorage (storage);

      /* getting rid of all the crap first */
      if( foreign_options )
            StorageCleanUp (&storage, foreign_options, CF_DISABLED_OPTION|CF_PHONY_OPTION|CF_FOREIGN_OPTION);

      DestroyConfig (config_reader);   
      return storage;
}      




/****************************************************************************************/
/*                        Assorted utility functions                                    */
/****************************************************************************************/
/* service functions */
void
FlushConfigBuffer (ConfigDef * config)
{
      config->buffer[0] = '\0';
      config->cursor = &(config->buffer[0]);
}

/***************************************************************************/
/***************************************************************************/
/*                   config Writing stuff                                  */
/***************************************************************************/
/* writer initialization */
ConfigDef    *
InitConfigWriter (char *myname, SyntaxDef * syntax, ConfigDataType type, ConfigData source)
{
#ifdef WITH_CONFIG_WRITER
      ConfigDef    *new_conf = NewConfig (myname, syntax, type, source, NULL, True);

      if (new_conf == NULL)
            return NULL;
      if (source.vptr != NULL)
      {
            if (type == CDT_Data)
            {
                  if ((new_conf->buffer_size = strlen (source.data)) > 0)
                  {
                        new_conf->buffer = safemalloc (++(new_conf->buffer_size));
                        strcpy (new_conf->buffer, source.data);
                  }
            } else
            {                                                        /* reading entire file in memory here */
                  struct stat   file_stats;

                  if (new_conf->fd != -1)
                        if (fstat (new_conf->fd, &file_stats) == 0)
                        {
                              if ((new_conf->buffer_size = file_stats.st_size) > 0)
                              {
                                    int           bytes_read;

                                    new_conf->buffer = (char *)safemalloc (++new_conf->buffer_size);
                                    if ((bytes_read = read (new_conf->fd, new_conf->buffer, file_stats.st_size)) > 0)
                                          new_conf->buffer[bytes_read] = '\0';
                                    else
                                    {
                                          free (new_conf->buffer);
                                          new_conf->buffer = NULL;
                                          new_conf->buffer_size = 0;
                                    }
                              } else
                              {                                      /* file is empty, so continuing onto creating a new file */
                                    new_conf->buffer = safemalloc (1);
                                    new_conf->buffer[0] = '\0';
                                    new_conf->buffer_size = 1;
                              }
                        }
            }
            if (new_conf->buffer == NULL)
            {
                  DestroyConfig (new_conf);
                  return NULL;
            }
            new_conf->bytes_in = new_conf->buffer_size - 1;

            new_conf->cursor = &(new_conf->buffer[0]);
      }

      return new_conf;
#else
      return NULL;
#endif
}

/***************************************************************************/
/* functions used in writing config */
#ifdef WITH_CONFIG_WRITER
/* Finds element in FreeStorage that represents this particular term
 * it then removes it from the list of elems and returns pointer to it
 */
FreeStorageElem *
FindTermAndRemove (FreeStorageElem ** storage, TermDef * pterm)
{
      FreeStorageElem **ppCurr = storage, *pRes;

      if (storage == NULL)
            return NULL;
      for (; *ppCurr != NULL; ppCurr = &((*ppCurr)->next))
            if ((*ppCurr)->term == pterm)
                  break;

      pRes = *ppCurr;
      if (pRes != NULL)
      {
            *ppCurr = pRes->next;
            pRes->next = NULL;
      }

      return pRes;
}

/* writes block of text in to the output buffer, enlarging it if needed.
 * it starts with block_start and end with block_end or '\0'
 * if block_end is NULL
 */
struct WriteBuffer
{
      char         *buffer;
      size_t        allocated, used;
};

void
WriteBlock (struct WriteBuffer *t_buffer, char *block_start, char *block_end)
{
      size_t        bytes_to_add;

      if (t_buffer == NULL || block_start == NULL)
            return;
      bytes_to_add = (block_end == NULL) ? strlen (block_start) : block_end - block_start;
      if (bytes_to_add <= 0)
            return;
      /*make sure we always have 1 byte left at the end ( for trailing 0 ) */
      /*                     and 1 byte left for file terminator           */
      if (t_buffer->allocated < t_buffer->used + bytes_to_add + 2)
      {                                                              /* growing buffer by 1/8 of it's size */
            size_t        add_size = t_buffer->used + bytes_to_add + 2 - t_buffer->allocated;

            if (add_size < (t_buffer->allocated >> 3))
                  add_size = (t_buffer->allocated >> 3);
            if (t_buffer->buffer != NULL && t_buffer->allocated > 0)
                  t_buffer->buffer = realloc (t_buffer->buffer, t_buffer->allocated + add_size);
            else
                  t_buffer->buffer = safemalloc (t_buffer->allocated + add_size);
            t_buffer->allocated += add_size;
      }
      memcpy (t_buffer->buffer + t_buffer->used, block_start, bytes_to_add);
      t_buffer->used += bytes_to_add;
}


char         *
terminate (char *ptr, char terminator)
{
      if (*ptr != terminator && (*ptr == '\0' && *(ptr - 1) != terminator))
            *(ptr++) = terminator;
      return ptr;
}

/*
 * creates string representing freeStorage's elem and writes it into the
 * output buffer
 */
static void
WriteFreeStorageElem (ConfigDef * config, struct WriteBuffer *t_buffer, FreeStorageElem * pElem, int level)
{
      static char  *elem_buf = NULL;
      static long   buf_allocated = 0;

      size_t        elem_buf_size = 0, params_size;
      register SyntaxDef *csyntax;
      register char *ptr;

      if (pElem == NULL || config->syntax == NULL)
      {
            if (elem_buf)
                  free (elem_buf);
            elem_buf = NULL;
            buf_allocated = 0;
            return;
      }
      if (pElem->term == NULL)
            return;

      csyntax = config->syntax;
      /* now lets approximate our memory size needed to accomodate complete config line : */
      /* ( allowing some slack just for simplier algorithm, since few bytes would not really matter ) */
      /* first goes constants (denoted as C# below) : */
      elem_buf_size += DISABLED_KEYWORD_SIZE       /* C1 */
            + 1                                                      /* C2 */
            + 1                                                      /* C3 */
            + pElem->argc                                      /* C4 */
            + 1;                                               /* C5 */
      /* now calculating variable lengths (denoted as V# below ): */
      if ((ptr = config->current_prepend) != NULL)    /* V1 */
            while (*(ptr++))
                  elem_buf_size++;

      ptr = csyntax->prepend_one;                        /* V2 */
      while (*(ptr++))
            elem_buf_size++;

      if ((ptr = config->myname) != NULL)          /* V3 */
            while (*(ptr++))
                  elem_buf_size++;

      if ((ptr = pElem->term->keyword) != NULL)  /* V4 */
            elem_buf_size += pElem->term->keyword_len;

      params_size = GetStringArraySize (pElem->argc, pElem->argv);      /* V5 */
      elem_buf_size += params_size;

      if ((ptr = csyntax->prepend_sub) != NULL)  /* V6 */
            while (*(ptr++))
                  elem_buf_size++;
      elem_buf_size++;

      if (elem_buf_size > 0)
      {
            register char *src;

            if (buf_allocated < elem_buf_size)
            {
                  buf_allocated = elem_buf_size + (elem_buf_size >> 3); /* to anticipate future buffer grows */
                  if (elem_buf != NULL)
                        free (elem_buf);
                  elem_buf = (char *)safemalloc (buf_allocated);
            }
            /*below is what's actually is using our memory: */
            ptr = elem_buf;
            if (config->current_prepend)           /*V1: config->current_prepend */
            {
                  src = config->current_prepend;
                  while (*src)
                        *(ptr++) = *(src++);
            }

            src = csyntax->prepend_one;                  /*V2: csyntax->prepend_one */
            while (*src)
                  *(ptr++) = *(src++);

            if (pElem->flags & CF_DISABLED_OPTION) /*C1: DISABLED_KEYWORD_SIZE */
            {
                  src = _disabled_keyword;
                  while (*src)
                        *(ptr++) = *(src++);
                  *(ptr++) = csyntax->token_separator;      /*C2: csyntax->token_separator */
            }else if (pElem->flags & CF_COMMENTED_OPTION) /*C1: DISABLED_KEYWORD_SIZE */
            {
                  *(ptr++) = COMMENTS_CHAR;
                  *(ptr++) = csyntax->token_separator;      /*C2: csyntax->token_separator */
            }

            if (!(pElem->term->flags & TF_NO_MYNAME_PREPENDING)
                  && !(pElem->flags & CF_PUBLIC_OPTION) && config->myname != NULL)
            {
                  *(ptr++) = MYNAME_CHAR;                /*C3: MYNAME_CHAR */
                  src = config->myname;                  /*V3: config->myname */
                  while (*src)
                        *(ptr++) = *(src++);
            }
            if (pElem->term->keyword)                    /*V4: pElem->term->keyword */
            {
                  src = pElem->term->keyword;
                  while (*src)
                        *(ptr++) = *(src++);
            }
            /* some beautification is in order here */
            if (params_size)
            {
                  int           i;

                  *(ptr++) = csyntax->token_separator;      /*C4: argc*csyntax->token_separator */
                  for (i = 0; i < pElem->argc; i++)
                        if ((src = pElem->argv[i]) != NULL)
                        {
                              if (i)
                                    *(ptr++) = csyntax->token_separator;
                              while (*src)
                                    *(ptr++) = *(src++);   /*V5: argv[i] */
                        }
            }
            *ptr = '\0';                                       /* so that termination will work */
            if (pElem->sub == NULL || pElem->term->sub_syntax == NULL)  /*C5: csyntax->terminator */
                  ptr = terminate (ptr, csyntax->terminator);
            else if (pElem->term->sub_syntax != NULL)
            {
                  if (pElem->term->sub_syntax->terminator == csyntax->terminator)
                        ptr = terminate (ptr, csyntax->terminator);
                  else if (csyntax->terminator == '\0' && pElem->term->sub_syntax->terminator != '\0')
                        ptr = terminate (ptr, pElem->term->sub_syntax->terminator);
            }

            if (*(ptr - 1) != csyntax->terminator) /* V6: min(csyntax->prepend_sub,1) */
            {                                                        /* we don't want for tokens to get joined together */
                  src = csyntax->prepend_sub;
                  if (src != NULL && *src != '\0')
                        while (*src)
                              *(ptr++) = *(src++);
                  else
                        ptr = terminate (ptr, csyntax->token_separator);
            }

            WriteBlock (t_buffer, elem_buf, ptr);
      }
      if (pElem->sub)
      {
            FreeStorageElem *psub;

            if (pElem->term->sub_syntax)
                  PushSyntax (config, pElem->term->sub_syntax);
            for (psub = pElem->sub; psub; psub = psub->next)
                  WriteFreeStorageElem (config, t_buffer, psub, level + 1);
            if (pElem->term->sub_syntax)
            {
                  PopSyntax (config);
                  if ((pElem->term->sub_syntax->file_terminator) != '\0')
                  {
                        *elem_buf = pElem->term->sub_syntax->file_terminator;
                        WriteBlock (t_buffer, elem_buf, elem_buf + 1);
                  }
            }
      }
}

void
WriteRemnants (ConfigDef * config, struct WriteBuffer *t_buffer, FreeStorageElem * pCurr)
{
/* > this function is writing options that are acually new for this config */
/* > as the result we want to write them into the begining of the file */
/* > so lets save current buffer contents, write remnants and then write buffer back */
/* I've changed that back to have later options override previous options */
/*                                                      Sasha
 */
/*  struct WriteBuffer rem_buffer ;
 *  int remnants_num = CountFreeStorageElems( pCurr );
 *
 *    if( remnants_num<=0 ) return ;
 *    rem_buffer.allocated = remnants_num*NORMAL_CONFIG_LINE+t_buffer->used+2 ;
 *    rem_buffer.buffer = (char*)safemalloc( rem_buffer.allocated );
 *    rem_buffer.used = 0 ;
 */
      for (; pCurr != NULL; pCurr = pCurr->next)
      {
            if (pCurr->term == NULL)
                  continue;
/*      WriteFreeStorageElem( config, &rem_buffer, pCurr );
 */ WriteFreeStorageElem (config, t_buffer, pCurr, 0);
      }

/*    WriteBlock( &rem_buffer, t_buffer->buffer, t_buffer->buffer+t_buffer->used );
 *    free( t_buffer->buffer );
 *    t_buffer->allocated = rem_buffer.allocated ;
 *    t_buffer->used = rem_buffer.used ;
 *    t_buffer->buffer = rem_buffer.buffer ;
 */
}

void
ScanAndWriteExistant (ConfigDef * config, FreeStorageElem ** storage, struct WriteBuffer *t_buffer, unsigned long flags)
{
      char         *noise_start = config->cursor;
      FreeStorageElem *pCurr;

      while (GetNextStatement (config, 0))
      {                                                              /* until not end of text */
            TermDef      *pterm;

            if (!(flags & WF_DISCARD_COMMENTS))
            {
                  WriteBlock (t_buffer, noise_start, config->tline_start);
                  noise_start = config->cursor;
            }
            if (IsForeignOption (config))
            {
                  if (!(flags & WF_DISCARD_FOREIGN))
                        WriteBlock (t_buffer, config->tline_start, config->cursor);
                  continue;
            }

            pterm = FindStatementTerm (config->tline, config->syntax);
            if (IsPublicOption (config))
            {
                  int           write_it = 1;

                  if (pterm)
                        write_it = !(pterm->flags & TF_NO_MYNAME_PREPENDING);
                  if (write_it)
                  {
                        if (!(flags & WF_DISCARD_PUBLIC))
                              WriteBlock (t_buffer, config->tline_start, config->cursor);
                        continue;
                  }
            }

            /* find term */
            if (pterm)
            {
                  pCurr = FindTermAndRemove (storage, pterm);
                  if (pCurr)
                        WriteFreeStorageElem (config, t_buffer, pCurr, 0);
            } else if (!(flags & WF_DISCARD_UNKNOWN))
                  WriteBlock (t_buffer, config->tline_start, config->cursor);

            DestroyFreeStorage (&pCurr);           /* no longer need it */
      }

      if (!(flags & WF_DISCARD_COMMENTS))
            WriteBlock (t_buffer, noise_start, config->cursor);

}

#endif
/***************************************************************************/
/* main writing procedure ( returns size of the data written )             */

long
WriteConfig (ConfigDef * config, FreeStorageElem ** storage,
                   ConfigDataType target_type, ConfigData *target, unsigned long flags)
{
#ifdef WITH_CONFIG_WRITER
      struct WriteBuffer t_buffer;
      int           t_fd = -1;

      if (config == NULL || storage == NULL || target == NULL)
            return -1;
      if (*storage == NULL)
            return -1;

      if (config->buffer_size <= 0)
      {
            t_buffer.allocated = MAXLINELENGTH;
            flags = WF_DISCARD_EVERYTHING;
      } else
            t_buffer.allocated = config->buffer_size;

      t_buffer.buffer = (char *)safemalloc (t_buffer.allocated);
      t_buffer.used = 0;
      /* scanning old file first to preserv comments */
      if (!(flags & WF_DISCARD_COMMENTS) ||
            !(flags & WF_DISCARD_UNKNOWN) || !(flags & WF_DISCARD_FOREIGN) || !(flags & WF_DISCARD_PUBLIC))
            ScanAndWriteExistant (config, storage, &t_buffer, flags);
      /* now writing remaining elements */
      WriteRemnants (config, &t_buffer, *storage);
      DestroyFreeStorage (storage);

      t_buffer.buffer[t_buffer.used] = config->syntax->file_terminator;
      t_buffer.buffer[t_buffer.used + 1] = '\0';

      if (config->syntax->file_terminator != '\0')
            t_buffer.used++;

      /* now saving buffer into file if we need to */
      switch (target_type)
      {
       case CDT_Filename:
             t_fd = open (target->filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
             break;
       case CDT_FilePtr:
     case CDT_FilePtrAndData:
         t_fd = fileno (target->fileptr);
             break;
       case CDT_FileDesc:
             t_fd = *(target->filedesc);
             break;
       case CDT_Data:
             target->data = t_buffer.buffer;
             return t_buffer.used;
      }
      if (t_fd != -1)
      {
            write (t_fd, t_buffer.buffer, t_buffer.used);
            if (target_type == CDT_Filename)
                  close (t_fd);
      }

      free (t_buffer.buffer);
      WriteFreeStorageElem (NULL, NULL, NULL, 0);     /* freeing statically allocated buffer */

      return t_buffer.used;
#else
      return 0;
#endif
}

Generated by  Doxygen 1.6.0   Back to index