blob: 6e052747515f9c26522c4016ff398115973545ae [file] [log] [blame]
/*
* hashdump.c : dumping and reading hash tables to/from files.
*
* ====================================================================
* Copyright (c) 2000-2002 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <stdio.h> /* for sprintf() */
#include <stdlib.h>
#include <apr_pools.h>
#include <apr_hash.h>
#include <apr_file_io.h>
#include "svn_types.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_hash.h"
#include "svn_io.h"
/*
* The format of a dumped hash table is:
*
* K <nlength>
* name (a string of <nlength> bytes, followed by a newline)
* V <vlength>
* val (a string of <vlength> bytes, followed by a newline)
* [... etc, etc ...]
* END
*
*
* (Yes, there is a newline after END.)
*
* For example:
*
* K 5
* color
* V 3
* red
* K 11
* wine review
* V 376
* A forthright entrance, yet coquettish on the tongue, its deceptively
* fruity exterior hides the warm mahagony undercurrent that is the
* hallmark of Chateau Fraisant-Pitre. Connoisseurs of the region will
* be pleased to note the familiar, subtle hints of mulberries and
* carburator fluid. Its confident finish is marred only by a barely
* detectable suggestion of rancid squid ink.
* K 5
* price
* V 8
* US $6.50
* END
*
*/
#define SVN_KEYLINE_MAXLEN 100 /* The longest a "key" line can be */
/*** Code. ***/
apr_size_t
svn_unpack_bytestring (char **returndata, void *value)
{
svn_stringbuf_t *valstring = (svn_stringbuf_t *) value;
*returndata = valstring->data;
return (size_t) valstring->len;
}
void *
svn_pack_bytestring (size_t len, const char *val, apr_pool_t *pool)
{
svn_stringbuf_t *valstring = apr_palloc (pool, sizeof (*valstring));
valstring->len = len;
valstring->blocksize = len;
valstring->data = (void *) val;
valstring->pool = pool;
return valstring;
}
apr_status_t
svn_hash_write (apr_hash_t *hash,
apr_size_t (*unpack_func) (char **unpacked_data, void *val),
apr_file_t *destfile,
apr_pool_t *pool)
{
apr_hash_index_t *this; /* current hash entry */
apr_status_t err;
char buf[SVN_KEYLINE_MAXLEN];
for (this = apr_hash_first (pool, hash); this; this = apr_hash_next (this))
{
const void *key;
void *val;
apr_ssize_t keylen;
size_t vallen;
int bytes_used;
char *valstring;
/* Get this key and val. */
apr_hash_this (this, &key, &keylen, &val);
/* Output name length, then name. */
err = apr_file_write_full (destfile, "K ", 2, NULL);
if (err) return err;
sprintf (buf, "%ld%n", (long int) keylen, &bytes_used);
err = apr_file_write_full (destfile, buf, bytes_used, NULL);
if (err) return err;
err = apr_file_write_full (destfile, "\n", 1, NULL);
if (err) return err;
err = apr_file_write_full (destfile, (char *) key, keylen, NULL);
if (err) return err;
err = apr_file_write_full (destfile, "\n", 1, NULL);
if (err) return err;
/* Output value length, then value. */
vallen = (size_t) (*unpack_func) (&valstring, val); /* secret decoder! */
err = apr_file_write_full (destfile, "V ", 2, NULL);
if (err) return err;
sprintf (buf, "%ld%n", (long int) vallen, &bytes_used);
err = apr_file_write_full (destfile, buf, bytes_used, NULL);
if (err) return err;
err = apr_file_write_full (destfile, "\n", 1, NULL);
if (err) return err;
err = apr_file_write_full (destfile, valstring, vallen, NULL);
if (err) return err;
err = apr_file_write_full (destfile, "\n", 1, NULL);
if (err) return err;
}
err = apr_file_write_full (destfile, "END\n", 4, NULL);
if (err) return err;
return APR_SUCCESS;
}
/* Read a line from FILE into BUF, but not exceeding *LIMIT bytes.
* Does not include newline, instead '\0' is put there.
* Length (as in strlen) is returned in *LIMIT.
* BUF should be pre-allocated.
* FILE should be already opened.
*
* (This is meant for reading length lines from hashdump files.)
*/
apr_status_t
svn_io_read_length_line (apr_file_t *file, char *buf, apr_size_t *limit)
{
apr_size_t i;
apr_status_t err;
char c;
for (i = 0; i < *limit; i++)
{
err = apr_file_getc (&c, file);
if (err)
return err; /* Note: this status code could be APR_EOF, which
is totally fine. The caller should be aware of
this. */
if (c == '\n')
{
buf[i] = '\0';
*limit = i;
return APR_SUCCESS;
}
else
{
buf[i] = c;
}
}
/* todo: make a custom error "SVN_LENGTH_TOO_LONG" or something? */
return SVN_WARNING;
}
apr_status_t
svn_hash_read (apr_hash_t *hash,
void * (*pack_func) (size_t len,
const char *val,
apr_pool_t *pool),
apr_file_t *srcfile,
apr_pool_t *pool)
{
apr_status_t err;
char buf[SVN_KEYLINE_MAXLEN];
apr_size_t num_read;
char c;
void *package;
int first_time = 1;
while (1)
{
/* Read a key length line. Might be END, though. */
apr_size_t len = sizeof(buf);
err = svn_io_read_length_line (srcfile, buf, &len);
if ((err == APR_EOF) && first_time)
/* We got an EOF on our very first attempt to read, which
means it's a zero-byte file. No problem, just go home. */
return APR_SUCCESS;
else if (err)
/* Any other circumstance is a genuine error. */
return err;
first_time = 0;
if ((len == 3)
&& (buf[0] == 'E') /* We've reached the end of the */
&& (buf[1] == 'N') /* dumped hash table, so leave. */
&& (buf[2] == 'D'))
{
return APR_SUCCESS;
}
else if ((buf[0] == 'K') && (buf[1] == ' '))
{
/* Get the length of the key */
size_t keylen = (size_t) atoi (buf + 2);
/* Now read that much into a buffer, + 1 byte for null terminator */
void *keybuf = apr_palloc (pool, keylen + 1);
err = apr_file_read_full (srcfile, keybuf, keylen, &num_read);
if (err) return err;
((char *) keybuf)[keylen] = '\0';
/* Suck up extra newline after key data */
err = apr_file_getc (&c, srcfile);
if (err) return err;
if (c != '\n') return SVN_ERR_MALFORMED_FILE;
/* Read a val length line */
len = sizeof(buf);
err = svn_io_read_length_line (srcfile, buf, &len);
if (err) return err;
if ((buf[0] == 'V') && (buf[1] == ' '))
{
/* Get the length of the value */
int vallen = atoi (buf + 2);
/* Again, 1 extra byte for the null termination. */
void *valbuf = apr_palloc (pool, vallen + 1);
err = apr_file_read_full (srcfile, valbuf, vallen, &num_read);
if (err) return err;
((char *) valbuf)[vallen] = '\0';
/* Suck up extra newline after val data */
err = apr_file_getc (&c, srcfile);
if (err) return err;
if (c != '\n') return SVN_ERR_MALFORMED_FILE;
/* Send the val data for packaging... */
package = (void *) (*pack_func) (vallen, valbuf, pool);
/* The Grand Moment: add a new hash entry! */
apr_hash_set (hash, keybuf, keylen, package);
}
else
{
return SVN_ERR_MALFORMED_FILE;
}
}
else
{
return SVN_ERR_MALFORMED_FILE;
}
} /* while (1) */
}
/*
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end:
*/