[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: String type madness

From: Ben Collins-Sussman <sussman_at_collab.net>
Date: 2001-07-26 18:11:06 CEST

Silly CVSWeb won't show head revisions if they're deleted. I got
these files by looking at the penultimate revisions.

------------- svn_parse.h

/* svn_parse: shared parsing routines for reading config files
 *
 * ================================================================
 * Copyright (c) 2000 Collab.Net. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 * any, must include the following acknowlegement: "This product includes
 * software developed by Collab.Net (http://www.Collab.Net/)."
 * Alternately, this acknowlegement may appear in the software itself, if
 * and wherever such third-party acknowlegements normally appear.
 *
 * 4. The hosted project names must not be used to endorse or promote
 * products derived from this software without prior written
 * permission. For written permission, please contact info@collab.net.
 *
 * 5. Products derived from this software may not use the "Tigris" name
 * nor may "Tigris" appear in their names without prior written
 * permission of Collab.Net.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ====================================================================
 *
 * This software may consist of voluntary contributions made by many
 * individuals on behalf of Collab.Net.
 */


#ifndef SVN_PARSE_H
#define SVN_PARSE_H

#include <svn_types.h>
#include <svn_string.h>
#include <svn_error.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_file_io.h>
#include <ctype.h> /* isspace() */

/*
   General Utility -- reads a config file, returns a hash of hashes.

   For file format, see `notes/svn-config-files'
*/

svn_error_t * svn_parse (ap_hash_t **returnhash,
                         const char *filename,
                         ap_pool_t *pool);

/* Prints a hash, assuming all keys/vals are (svn_string_t *) */

void svn_hash_print (ap_hash_t *hash, FILE *stream);

/* Prints uberhash returned from svn_parse(),
   assuming each key is (svn_string_t *)
   and each val is printable by svn_hash_print(). */

void svn_uberhash_print (ap_hash_t *uberhash, FILE *stream);

#endif /* SVN_PARSE_H */


/*
 * local variables:
 * eval: (load-file "../svn-dev.el")
 * end: */

------------- svn_parse.c

/* svn_parse: shared parsing routines for reading config files
 *
 * ================================================================
 * Copyright (c) 2000 CollabNet. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 * any, must include the following acknowlegement: "This product includes
 * software developed by CollabNet (http://www.Collab.Net)."
 * Alternately, this acknowlegement may appear in the software itself, if
 * and wherever such third-party acknowlegements normally appear.
 *
 * 4. The hosted project names must not be used to endorse or promote
 * products derived from this software without prior written
 * permission. For written permission, please contact info@collab.net.
 *
 * 5. Products derived from this software may not use the "Tigris" name
 * nor may "Tigris" appear in their names without prior written
 * permission of CollabNet.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL COLLABNET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ====================================================================
 *
 * This software may consist of voluntary contributions made by many
 * individuals on behalf of CollabNet.
 */


#include "svn_parse.h"

/*
   NOT EXPORTED.

   Input: an open file, a bytestring ptr, and a pool.

   Returns: either APR_EOF or APR_SUCCESS, and a filled-in bytestring
             containing the next line of the file.

         (Note that the same bytestring can be reused in multiple
         calls to this routine, because the bytestring is cleared at
         the beginning.)
*/

apr_status_t
svn__my_readline (apr_file_t *file, svn_string_t *line, apr_pool_t *pool)
{
  char c;
  apr_status_t result;

  svn_string_setempty (line); /* clear the bytestring first! */

  while (1)
    {
      result = apr_getc (&c, file); /* read a byte from the file */

      if (result == APR_EOF) /* file is finished. */
        {
          return APR_EOF;
        }
      
      if (c == '\n') /* line is finished. */
        {
          /* store the newline in our bytestring (important!) */
          svn_string_appendbytes (line, &c, 1);

          return APR_SUCCESS;
        }
      
      else /* otherwise, just append this byte to the bytestring */
        {
          svn_string_appendbytes (line, &c, 1);
        }
    }
}

/*
   NOT EXPORTED.

   Input: a bytestring, a handle for returning a bytestring, the
           starting search offset, search character, and pool

   Returns: 1. the offset of the search character (-1 if no match)
             2. a newly allocated substring in "substr" (NULL if no match)
                * This substring starts at `start' and goes to the offset
                * This substring has no whitespace at either end

     If used repeatedly, this routine is like a poor man's `split'
     (combined with chomp).
*/

int
svn__slurp_to (const svn_string_t *searchstr,
           svn_string_t **substr,
           const size_t start,
           const char sc,
           apr_pool_t *pool)
{
  int i;

  /* Create a new bytestring */
  *substr = svn_string_create ("<nobody home>", pool);
  svn_string_setempty (*substr);

  for (i = start; i < searchstr->len; i++)
    {
      if (searchstr->data[i] == sc)
        {
          /* printf ("found character '%c' at offset %d\n", sc, i);*/

          svn_string_appendbytes (*substr, /* new substring */
                                  searchstr->data + start,/* start copy */
                                  (i - start)); /* number to copy */
          
          svn_string_strip_whitespace (*substr);

          return i;
        }
    }

  /* If we get here, then the bytestring doesn't contain our search
     character. This is bogus. */
  
  *substr = NULL;
  return -1;
}

/*
   svn_parse() (finally)

   Input: a filename and pool, pointer to a hash

   Output: a pointer to a hash of hashes, all built within the pool

   This routine parses a file which conforms to the standard
   Subversion config file format (look in notes/).

   The hash returned is a mapping from section-names to hash pointers;
   each hash contains the keys/vals for each section. All
   section-names, keys and vals are stored as svn_string_t pointers.
   (These bytestrings are allocated in the same pool as the hashes.)

   This routine makes no attempt to understand the sections, keys or
   values. :) */

svn_error_t *
svn_parse (apr_hash_t **uberhash, const char *filename, apr_pool_t *pool)
{
  apr_hash_t *current_hash; /* the hash we're currently storing vals in */

  apr_pool_t *scratchpool;
  svn_string_t *currentline;
  apr_status_t result;
  apr_file_t *file = NULL;

  /* Create our uberhash */
  *uberhash = apr_make_hash (pool);

  /* Open the config file */
  result = apr_open (&file,
                    filename,
                    APR_READ,
                    APR_OS_DEFAULT, /*TODO: WHAT IS THIS? */
                    pool);
  
  if (result != APR_SUCCESS)
    {
      char *finalmsg; /* TODO: use apr's sprintf() thingie here */
      svn_string_t *msg = svn_string_create
        ("svn_parse(): can't open for reading, file ", pool);
      svn_string_appendstr (msg, filename);

      return svn_error_create (result, NULL, NULL, pool, msg->data);
    }

  /* Create a scratch memory pool for buffering our file as we read it */
  scratchpool = svn_pool_create (pool);

  /* Create a bytestring to hold the current line of file */
  currentline = svn_string_create ("<nobody home>", scratchpool);

  /* Now start scanning our file, one line at a time */

  while (svn__my_readline (file, currentline, scratchpool) != APR_EOF)
    {
      char c;
      size_t offset = svn_string_first_non_whitespace (currentline);

      if (offset == currentline->len)
        {
          /* whole line is whitespace, read next line! */
          continue;
        }
      
      c = currentline->data[offset]; /* our first non-white character */

      switch (c)
        {
        case '#':
          {
            /* It's a comment line, so read next line! */
            continue;
          };

        case '[':
          {
            /* It's a new section! */

            /* Create new hash to hold this section's keys/vals */
            apr_hash_t *new_section_hash = apr_make_hash (pool);

            /* Slurp up the section name */
            svn_string_t *new_section;

            svn__slurp_to (currentline, /* search current line */
                           &new_section, /* place new substring here */
                           offset + 1, /* start searching past the '[' */
                           ']', /* look for this ending character */
                           pool); /* build our substring in this pool */
           
            if (new_section == NULL) /* couldn't find a ']' ! */
              {
                policy->warning
                  (policy->data,
                   "svn_parse(): skipping malformed line: `%s'",
                   currentline->data, pool);
                break;
              }
                                        
            /* printf ("Found new section: `");
               svn_string_print (new_section, stdout, FALSE, FALSE);
               printf ("'\n"); */

            /* make this new hash the "active" hash for new keys/vals */
            current_hash = new_section_hash;

            /* store this new hash in our uberhash */
            apr_hash_set (*uberhash,
                         new_section->data, /* key: bytestring */
                         new_section->len, /* the length of the key */
                         new_section_hash); /* val: ptr to the new hash */
            break;
          }

        default:
          {
            /* If it's not a blank line, comment line, or section line,
               then it MUST be a key : val line! */

            /* Slurp up the key by searching for a colon */

            svn_string_t *new_key, *new_val;
            size_t local_offset;

            local_offset = svn__slurp_to (currentline, /* search line */
                                          &new_key, /* put substring here */
                                          offset, /* start at this offset */
                                          ':', /* look for a colon */
                                          pool); /* build substr here */
            
            if (new_key == NULL) /* didn't find a colon! */
              {
                policy->warning
                  (policy->data,
                   "svn_parse(): skipping malformed line: `%s'",
                   currentline->data);
                break;
              }

            /* Now slurp up the value, starting just past the colon */

            svn__slurp_to (currentline,
                       &new_val,
                       local_offset + 1,
                       '\n',
                       pool);

            /* printf ("Key: `");
                svn_string_print (new_key, stdout, FALSE, FALSE);
                printf ("'\n");
                printf ("Val: `");
                svn_string_print (new_val, stdout, FALSE, FALSE);
                printf ("'\n"); */

            /* Should we check for a NULL result from svn__slurp_to?
               What are the chances it's not going to find a newline? :)
            */

            /* Store key and val in the currently active hash */
            apr_hash_set (current_hash,
                         new_key->data, /* key: bytestring data */
                         new_key->len, /* length of key */
                         new_val); /* val: ptr to bytestring */
            break;
          } /* default: */
        } /* switch (c) */
    } /* while (readline) */

     
  /* Close the file and free our scratchpool */

  result = apr_close (file);
  if (result != APR_SUCCESS)
    {
      policy->warning
        (policy->data,
         "svn_parse(): can't close file: `%s'", filename->data);
    }
  
  apr_destroy_pool (scratchpool);

  return SVN_NO_ERROR;
}

/* Convenience Routine: pretty-print an apr_hash_t.

     (ASSUMING that all keys and vals are of type (svn_string_t *) )

*/

void
svn_hash_print (apr_hash_t *hash, FILE *stream)
{
  apr_hash_index_t *hash_index; /* this represents a hash entry */
  void *key, *val;
  size_t keylen;
  svn_string_t keystring, *valstring;

  fprintf (stream, "\n-----> Printing hash:\n");

  for (hash_index = apr_hash_first (hash); /* get first hash entry */
       hash_index; /* NULL if out of entries */
       hash_index = apr_hash_next (hash_index)) /* get next hash entry */
    {
      /* Retrieve key and val from current hash entry */
      apr_hash_this (hash_index, &key, &keylen, &val);

      /* Cast things nicely */
      keystring.data = key;
      keystring.len = keylen;
      keystring.blocksize = keylen;

      valstring = val;

      /* Print them out nicely */
      fprintf (stream, "Key: `");
      /* TODO: svn_string_print (&keystring, stream, FALSE, FALSE); */
      fprintf (stream, "', ");

      fprintf (stream, "Val: `");
      /* TODO: svn_string_print (valstring, stream, FALSE, FALSE); */
      fprintf (stream, "'\n");
    }
  
  fprintf (stream, "\n");
}

/* Convenience Routine: pretty-print "uberhash" from svn_parse().

   (ASSUMING that all keys are (svn_string_t *),
                  all vals are (apr_hash_t *) printable by svn_hash_print() )
*/

void
svn_uberhash_print (apr_hash_t *uberhash, FILE *stream)
{
  apr_hash_index_t *hash_index; /* this represents a hash entry */
  void *key, *val;
  size_t keylen;
  svn_string_t keystring;
  apr_hash_t *valhash;

  fprintf (stream, "\n-> Printing Uberhash:\n");

  for (hash_index = apr_hash_first (uberhash); /* get first hash entry */
       hash_index; /* NULL if out of entries */
       hash_index = apr_hash_next (hash_index)) /* get next hash entry */
    {
      /* Retrieve key and val from current hash entry */
      apr_hash_this (hash_index, &key, &keylen, &val);

      /* Cast things nicely */
      keystring.data = key;
      keystring.len = keylen;
      keystring.blocksize = keylen;

      valhash = val;

      /* Print them out nicely */
      fprintf (stream, "---> Hashname: `");
      /* TODO: svn_string_print (&keystring, stream, FALSE, FALSE); */
      fprintf (stream, "'\n");

      svn_hash_print (valhash, stream);
    }
  
  fprintf (stream, "\nUberhash printing complete.\n\n");
}


/*
 * local variables:
 * eval: (load-file "../svn-dev.el")
 * end: */

------------- parsetest.c

/* Testing svn_parse() */

#include <stdio.h>
#include "svn_parse.h"

int
main ()
{
  apr_hash_t *configdata;
  apr_pool_t *pool;
  svn_error_t *error;

  /* Initialize APR (Apache pools) */
  if (apr_initialize () != APR_SUCCESS)
    {
      printf ("apr_initialize() failed.\n");
      exit (1);
    }

  pool = svn_pool_create (NULL);

  /* Parse the file "./configfile" */

  error =
    svn_parse (&configdata, svn_string_create ("configfile", pool), pool);

  if (error) {
    svn_handle_error (error);
  }

  /* Print out our configdata uber-hash */

  svn_uberhash_print (configdata, stdout);

  /* If we were an application using libsvn_svr, we would now pass
     this uber-hash to svn_init() to get a `svn_policies_t' structure.
     We would then use this structure for all our wrappered filesystem
     calls. */

  /* Clean up our memory pool and apr */
  apr_destroy_pool (pool);
  apr_terminate ();

  printf ("Test complete, exiting cleanly.\n\n");
  return 0;
}

------------- configfile

# This is a sample Subversion config file

# Nicely Formatted Part

[Grades]

Joe : A
Mary : B-
Alice : A+

[Doughnuts]

Powdered : $.39
Chocolate : $1.59 a dozen

# BaDlY FoRMatTed paRT

    [foosection]
                giraffe : 17
  elephant: green

    # Yummy section
[unfinished section

 [barsection ]
  3.14159 :pi
 eee : 2.71
        ghoti : phish

# Really important section!

syntaxerror

   [ bazsection ]
airplane: bahamas

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Sat Oct 21 14:36:33 2006

This is an archived mail posted to the Subversion Dev mailing list.