blob: 5d6c19068607286a5769342542ab7fa493457375 [file] [log] [blame]
/* 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.
*/
#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.)
*/
ap_status_t
svn__my_readline (ap_file_t *FILE, svn_string_t *line, ap_pool_t *pool)
{
char c;
ap_status_t result;
svn_string_setempty (line); /* clear the bytestring first! */
while (1)
{
result = ap_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, pool);
return APR_SUCCESS;
}
else /* otherwise, just append this byte to the bytestring */
{
svn_string_appendbytes (line, &c, 1, pool);
}
}
}
/*
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,
ap_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 */
pool);
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 (ap_hash_t **uberhash, svn_string_t *filename, ap_pool_t *pool)
{
ap_hash_t *current_hash; /* the hash we're currently storing vals in */
ap_pool_t *scratchpool;
svn_string_t *currentline;
ap_status_t result;
ap_file_t *FILE = NULL;
/* Create our uberhash */
*uberhash = ap_make_hash (pool);
/* Open the config file */
result = ap_open (&FILE,
svn_string_2cstring (filename, pool),
APR_READ,
APR_OS_DEFAULT, /*TODO: WHAT IS THIS? */
pool);
if (result != APR_SUCCESS)
{
svn_string_t *msg = svn_string_create
("svn_parse(): can't open for reading, file ", pool);
svn_string_appendstr (msg, filename, pool);
return (svn_create_error (result, SVN_NON_FATAL, msg, pool));
}
/* Create a scratch memory pool for buffering our file as we read it */
if ((result = ap_create_pool (&scratchpool, NULL)) != APR_SUCCESS)
{
return
(svn_create_error
(result, SVN_FATAL,
svn_string_create ("svn_parse(): fatal: can't create scratchpool",
pool), 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 */
ap_hash_t *new_section_hash = ap_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 ']' ! */
{
svn_string_t *msg =
svn_string_create
("svn_parse(): warning: skipping malformed line: ", pool);
svn_string_appendstr (msg, currentline, pool);
/* Instead of returning an error, just print warning */
svn_handle_error (svn_create_error
(SVN_ERR_MALFORMED_LINE, SVN_NON_FATAL,
msg, 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 */
ap_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 current 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! */
{
svn_string_t *msg =
svn_string_create
("svn_parse(): warning: skipping malformed line: ", pool);
svn_string_appendstr (msg, currentline, pool);
/* Instead of returning an error, just print warning */
svn_handle_error (svn_create_error
(SVN_ERR_MALFORMED_LINE, SVN_NON_FATAL,
msg, pool));
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 */
ap_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 = ap_close (FILE);
if (result != APR_SUCCESS)
{
svn_string_t *msg = svn_string_create
("svn_parse(): warning: can't close file ", pool);
svn_string_appendstr (msg, filename, pool);
/* Not fatal, just annoying. Send a warning instead returning error. */
svn_handle_error (svn_create_error (result, SVN_NON_FATAL, msg, pool));
}
ap_destroy_pool (scratchpool);
/* Return success */
return 0;
}
/* Convenience Routine: pretty-print an ap_hash_t.
(ASSUMING that all keys and vals are of type (svn_string_t *) )
*/
void
svn_hash_print (ap_hash_t *hash, FILE *stream)
{
ap_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 = ap_hash_first (hash); /* get first hash entry */
hash_index; /* NULL if out of entries */
hash_index = ap_hash_next (hash_index)) /* get next hash entry */
{
/* Retrieve key and val from current hash entry */
ap_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: `");
svn_string_print (&keystring, stream, FALSE, FALSE);
fprintf (stream, "', ");
fprintf (stream, "Val: `");
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 (ap_hash_t *) printable by svn_hash_print() )
*/
void
svn_uberhash_print (ap_hash_t *uberhash, FILE *stream)
{
ap_hash_index_t *hash_index; /* this represents a hash entry */
void *key, *val;
size_t keylen;
svn_string_t keystring;
ap_hash_t *valhash;
fprintf (stream, "\n-> Printing Uberhash:\n");
for (hash_index = ap_hash_first (uberhash); /* get first hash entry */
hash_index; /* NULL if out of entries */
hash_index = ap_hash_next (hash_index)) /* get next hash entry */
{
/* Retrieve key and val from current hash entry */
ap_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: `");
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: */