You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/15 21:32:38 UTC
svn commit: r997472 [13/41] - in /subversion/branches/py-tests-as-modules:
./ build/ build/ac-macros/ build/generator/ build/generator/templates/
contrib/server-side/ notes/ notes/tree-conflicts/ notes/wc-ng/
subversion/bindings/javahl/native/ subversi...
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_diff/parse-diff.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_diff/parse-diff.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_diff/parse-diff.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_diff/parse-diff.c Wed Sep 15 19:32:26 2010
@@ -21,7 +21,6 @@
* ====================================================================
*/
-#include <limits.h> /* for ULONG_MAX */
#include <stdlib.h>
#include <string.h>
@@ -29,34 +28,116 @@
#include "svn_error.h"
#include "svn_io.h"
#include "svn_pools.h"
+#include "svn_string.h"
#include "svn_utf.h"
#include "svn_dirent_uri.h"
#include "svn_diff.h"
+#include "private/svn_eol_private.h"
+
/* Helper macro for readability */
#define starts_with(str, start) \
(strncmp((str), (start), strlen(start)) == 0)
+struct svn_diff_hunk_t {
+ /* Hunk texts (see include/svn_diff.h). */
+ svn_stream_t *diff_text;
+ svn_stream_t *original_text;
+ svn_stream_t *modified_text;
+
+ /* The patch this hunk belongs to. */
+ svn_patch_t *patch;
+
+ /* Hunk ranges as they appeared in the patch file.
+ * All numbers are lines, not bytes. */
+ svn_linenum_t original_start;
+ svn_linenum_t original_length;
+ svn_linenum_t modified_start;
+ svn_linenum_t modified_length;
+
+ /* Number of lines of leading and trailing hunk context. */
+ svn_linenum_t leading_context;
+ svn_linenum_t trailing_context;
+};
+
+svn_error_t *
+svn_diff_hunk_reset_diff_text(const svn_diff_hunk_t *hunk)
+{
+ return svn_error_return(svn_stream_reset(hunk->diff_text));
+}
+
+svn_error_t *
+svn_diff_hunk_reset_original_text(const svn_diff_hunk_t *hunk)
+{
+ if (hunk->patch->reverse)
+ return svn_error_return(svn_stream_reset(hunk->modified_text));
+ else
+ return svn_error_return(svn_stream_reset(hunk->original_text));
+}
+
+svn_error_t *
+svn_diff_hunk_reset_modified_text(const svn_diff_hunk_t *hunk)
+{
+ if (hunk->patch->reverse)
+ return svn_error_return(svn_stream_reset(hunk->original_text));
+ else
+ return svn_error_return(svn_stream_reset(hunk->modified_text));
+}
+
+svn_linenum_t
+svn_diff_hunk_get_original_start(const svn_diff_hunk_t *hunk)
+{
+ return hunk->patch->reverse ? hunk->modified_start : hunk->original_start;
+}
+
+svn_linenum_t
+svn_diff_hunk_get_original_length(const svn_diff_hunk_t *hunk)
+{
+ return hunk->patch->reverse ? hunk->modified_length : hunk->original_length;
+}
+
+svn_linenum_t
+svn_diff_hunk_get_modified_start(const svn_diff_hunk_t *hunk)
+{
+ return hunk->patch->reverse ? hunk->original_start : hunk->modified_start;
+}
+
+svn_linenum_t
+svn_diff_hunk_get_modified_length(const svn_diff_hunk_t *hunk)
+{
+ return hunk->patch->reverse ? hunk->original_length : hunk->modified_length;
+}
+
+svn_linenum_t
+svn_diff_hunk_get_leading_context(const svn_diff_hunk_t *hunk)
+{
+ return hunk->leading_context;
+}
+
+svn_linenum_t
+svn_diff_hunk_get_trailing_context(const svn_diff_hunk_t *hunk)
+{
+ return hunk->trailing_context;
+}
+
/* Try to parse a positive number from a decimal number encoded
* in the string NUMBER. Return parsed number in OFFSET, and return
* TRUE if parsing was successful. */
static svn_boolean_t
parse_offset(svn_linenum_t *offset, const char *number)
{
- apr_int64_t parsed_offset;
+ svn_error_t *err;
+ apr_uint64_t val;
- errno = 0; /* apr_atoi64() in APR-0.9 does not always set errno */
- parsed_offset = apr_atoi64(number);
- if (errno == ERANGE || parsed_offset < 0)
- return FALSE;
+ err = svn_cstring_strtoui64(&val, number, 0, SVN_LINENUM_MAX_VALUE, 10);
+ if (err)
+ {
+ svn_error_clear(err);
+ return FALSE;
+ }
- /* In case we cannot fit 64 bits into an svn_linenum_t,
- * check for overflow. */
- if (sizeof(svn_linenum_t) < sizeof(parsed_offset) &&
- parsed_offset > SVN_LINENUM_MAX_VALUE)
- return FALSE;
+ *offset = (svn_linenum_t)val;
- *offset = parsed_offset;
return TRUE;
}
@@ -105,8 +186,8 @@ parse_range(svn_linenum_t *start, svn_li
* If REVERSE is TRUE, invert the hunk header while parsing it.
* Do all allocations in POOL. */
static svn_boolean_t
-parse_hunk_header(const char *header, svn_hunk_t *hunk,
- const char *atat, svn_boolean_t reverse, apr_pool_t *pool)
+parse_hunk_header(const char *header, svn_diff_hunk_t *hunk,
+ const char *atat, apr_pool_t *pool)
{
const char *p;
svn_stringbuf_t *range;
@@ -124,7 +205,7 @@ parse_hunk_header(const char *header, sv
p++;
while (*p && *p != ' ')
{
- svn_stringbuf_appendbytes(range, p, 1);
+ svn_stringbuf_appendbyte(range, *p);
p++;
}
if (*p != ' ')
@@ -132,16 +213,8 @@ parse_hunk_header(const char *header, sv
return FALSE;
/* Try to parse the first range. */
- if (reverse)
- {
- if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
- return FALSE;
- }
- else
- {
- if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
- return FALSE;
- }
+ if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
+ return FALSE;
/* Clear the stringbuf so we can reuse it for the second range. */
svn_stringbuf_setempty(range);
@@ -153,7 +226,7 @@ parse_hunk_header(const char *header, sv
p++;
while (*p && *p != ' ')
{
- svn_stringbuf_appendbytes(range, p, 1);
+ svn_stringbuf_appendbyte(range, *p);
p++;
}
if (*p != ' ')
@@ -169,100 +242,238 @@ parse_hunk_header(const char *header, sv
* but we ignore that. */
/* Try to parse the second range. */
- if (reverse)
- {
- if (! parse_range(&hunk->original_start, &hunk->original_length, range->data))
- return FALSE;
- }
- else
- {
- if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
- return FALSE;
- }
+ if (! parse_range(&hunk->modified_start, &hunk->modified_length, range->data))
+ return FALSE;
/* Hunk header is good. */
return TRUE;
}
-/* A stream line-filter which allows only original text from a hunk,
- * and filters special lines (which start with a backslash). */
+/* Set *EOL to the first end-of-line string found in the stream
+ * accessed through READ_FN, MARK_FN and SEEK_FN, whose stream baton
+ * is BATON. Leave the stream read position unchanged.
+ * Allocate *EOL statically; POOL is a scratch pool. */
static svn_error_t *
-original_line_filter(svn_boolean_t *filtered, const char *line, void *baton,
- apr_pool_t *scratch_pool)
+scan_eol(const char **eol, svn_stream_t *stream, apr_pool_t *pool)
{
- *filtered = (line[0] == '+' || line[0] == '\\');
- return SVN_NO_ERROR;
-}
+ const char *eol_str;
+ svn_stream_mark_t *mark;
-/* A stream line-filter which allows only modified text from a hunk,
- * and filters special lines (which start with a backslash). */
-static svn_error_t *
-modified_line_filter(svn_boolean_t *filtered, const char *line, void *baton,
- apr_pool_t *scratch_pool)
-{
- *filtered = (line[0] == '-' || line[0] == '\\');
- return SVN_NO_ERROR;
-}
+ SVN_ERR(svn_stream_mark(stream, &mark, pool));
-/** line-transformer callback to shave leading diff symbols. */
-static svn_error_t *
-remove_leading_char_transformer(svn_stringbuf_t **buf,
- const char *line,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- if (line[0] == '+' || line[0] == '-' || line[0] == ' ')
- *buf = svn_stringbuf_create(line + 1, result_pool);
- else
- *buf = svn_stringbuf_create(line, result_pool);
+ eol_str = NULL;
+ while (! eol_str)
+ {
+ char buf[512];
+ apr_size_t len;
+
+ len = sizeof(buf);
+ SVN_ERR(svn_stream_read(stream, buf, &len));
+ if (len == 0)
+ break; /* EOF */
+ eol_str = svn_eol__detect_eol(buf, buf + len);
+ }
+
+ SVN_ERR(svn_stream_seek(stream, mark));
+
+ *eol = eol_str;
return SVN_NO_ERROR;
}
-/** line-transformer callback to reverse a diff text. */
+/* A helper function similar to svn_stream_readline_detect_eol(),
+ * suitable for reading original or modified hunk text from a STREAM
+ * which has been mapped onto a hunk region within a unidiff patch file.
+ *
+ * Allocate *STRINGBUF in RESULT_POOL, and read into it one line from STREAM.
+ *
+ * STREAM is expected to contain unidiff text.
+ * Leading unidiff symbols ('+', '-', and ' ') are removed from the line,
+ * Any lines commencing with the VERBOTEN character are discarded.
+ * VERBOTEN should be '+' or '-', depending on which form of hunk text
+ * is being read.
+ *
+ * The line-terminator is detected automatically and stored in *EOL
+ * if EOL is not NULL. If EOF is reached and the stream does not end
+ * with a newline character, and EOL is not NULL, *EOL is set to NULL.
+ *
+ * SCRATCH_POOL is used for temporary allocations.
+ */
static svn_error_t *
-reverse_diff_transformer(svn_stringbuf_t **buf,
- const char *line,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+hunk_readline(svn_stream_t *stream,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ char verboten,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_hunk_t hunk;
+ svn_stringbuf_t *str;
+ apr_pool_t *iterpool;
+ svn_boolean_t filtered;
+ const char *eol_str;
- /* ### Pass the already parsed hunk via the baton?
- * ### Maybe we should really make svn_stream_readline() a proper stream
- * ### method and override it instead of adding special callbacks? */
- if (parse_hunk_header(line, &hunk, "@@", FALSE, scratch_pool))
+ *eof = FALSE;
+
+ iterpool = svn_pool_create(scratch_pool);
+ do
+ {
+ apr_size_t numbytes;
+ const char *match;
+ char c;
+
+ svn_pool_clear(iterpool);
+
+ /* Since we're reading one character at a time, let's at least
+ optimize for the 90% case. 90% of the time, we can avoid the
+ stringbuf ever having to realloc() itself if we start it out at
+ 80 chars. */
+ str = svn_stringbuf_create_ensure(80, iterpool);
+
+ SVN_ERR(scan_eol(&eol_str, stream, iterpool));
+ if (eol)
+ *eol = eol_str;
+ if (eol_str == NULL)
+ {
+ /* No newline until EOF, EOL_STR can be anything. */
+ eol_str = APR_EOL_STR;
+ }
+
+ /* Read into STR up to and including the next EOL sequence. */
+ match = eol_str;
+ numbytes = 1;
+ while (*match)
+ {
+ SVN_ERR(svn_stream_read(stream, &c, &numbytes));
+ if (numbytes != 1)
+ {
+ /* a 'short' read means the stream has run out. */
+ *eof = TRUE;
+ /* We know we don't have a whole EOL sequence, but ensure we
+ * don't chop off any partial EOL sequence that we may have. */
+ match = eol_str;
+ /* Process this short (or empty) line just like any other
+ * except with *EOF set. */
+ break;
+ }
+
+ if (c == *match)
+ match++;
+ else
+ match = eol_str;
+
+ svn_stringbuf_appendbyte(str, c);
+ }
+
+ svn_stringbuf_chop(str, match - eol_str);
+ filtered = (str->data[0] == verboten || str->data[0] == '\\');
+ }
+ while (filtered && ! *eof);
+ /* Not destroying the iterpool just yet since we still need STR
+ * which is allocated in it. */
+
+ if (filtered)
{
- *buf = svn_stringbuf_createf(result_pool,
- "@@ -%lu,%lu +%lu,%lu @@",
- hunk.modified_start,
- hunk.modified_length,
- hunk.original_start,
- hunk.original_length);
+ /* EOF, return an empty string. */
+ *stringbuf = svn_stringbuf_create_ensure(0, result_pool);
}
- else if (parse_hunk_header(line, &hunk, "##", FALSE, scratch_pool))
+ else if (str->data[0] == '+' || str->data[0] == '-' || str->data[0] == ' ')
{
- *buf = svn_stringbuf_createf(result_pool,
- "## -%lu,%lu +%lu,%lu ##",
- hunk.modified_start,
- hunk.modified_length,
- hunk.original_start,
- hunk.original_length);
+ /* Shave off leading unidiff symbols. */
+ *stringbuf = svn_stringbuf_create(str->data + 1, result_pool);
}
- else if (line[0] == '+')
+ else
{
- *buf = svn_stringbuf_create(line, result_pool);
- (*buf)->data[0] = '-';
+ /* Return the line as-is. */
+ *stringbuf = svn_stringbuf_dup(str, result_pool);
}
- else if (line[0] == '-')
+
+ /* Done. RIP iterpool. */
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_diff_hunk_readline_original_text(const svn_diff_hunk_t *hunk,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ return svn_error_return(hunk_readline(hunk->patch->reverse ?
+ hunk->modified_text :
+ hunk->original_text,
+ stringbuf, eol, eof,
+ hunk->patch->reverse ? '-' : '+',
+ result_pool, scratch_pool));
+}
+
+svn_error_t *
+svn_diff_hunk_readline_modified_text(const svn_diff_hunk_t *hunk,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ return svn_error_return(hunk_readline(hunk->patch->reverse ?
+ hunk->original_text :
+ hunk->modified_text,
+ stringbuf, eol, eof,
+ hunk->patch->reverse ? '+' : '-',
+ result_pool, scratch_pool));
+}
+
+svn_error_t *
+svn_diff_hunk_readline_diff_text(const svn_diff_hunk_t *hunk,
+ svn_stringbuf_t **stringbuf,
+ const char **eol,
+ svn_boolean_t *eof,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_diff_hunk_t dummy;
+ svn_stringbuf_t *line;
+
+ SVN_ERR(svn_stream_readline_detect_eol(hunk->diff_text, &line, eol, eof,
+ result_pool));
+
+ if (hunk->patch->reverse)
{
- *buf = svn_stringbuf_create(line, result_pool);
- (*buf)->data[0] = '+';
+ if (parse_hunk_header(line->data, &dummy, "@@", scratch_pool))
+ {
+ /* Line is a hunk header, reverse it. */
+ *stringbuf = svn_stringbuf_createf(result_pool,
+ "@@ -%lu,%lu +%lu,%lu @@",
+ hunk->modified_start,
+ hunk->modified_length,
+ hunk->original_start,
+ hunk->original_length);
+ }
+ else if (parse_hunk_header(line->data, &dummy, "##", scratch_pool))
+ {
+ /* Line is a hunk header, reverse it. */
+ *stringbuf = svn_stringbuf_createf(result_pool,
+ "## -%lu,%lu +%lu,%lu ##",
+ hunk->modified_start,
+ hunk->modified_length,
+ hunk->original_start,
+ hunk->original_length);
+ }
+ else
+ {
+ if (line->data[0] == '+')
+ line->data[0] = '-';
+ else if (line->data[0] == '-')
+ line->data[0] = '+';
+
+ *stringbuf = line;
+ }
}
else
- *buf = svn_stringbuf_create(line, result_pool);
+ *stringbuf = line;
return SVN_NO_ERROR;
}
@@ -272,6 +483,8 @@ static svn_error_t *
parse_prop_name(const char **prop_name, const char *header,
const char *indicator, apr_pool_t *result_pool)
{
+ /* ### This can fail if the filename cannot be represented in the current
+ * ### locale's encoding. */
SVN_ERR(svn_utf_cstring_to_utf8(prop_name,
header + strlen(indicator),
result_pool));
@@ -283,18 +496,20 @@ parse_prop_name(const char **prop_name,
}
/* Return the next *HUNK from a PATCH, using STREAM to read data
- * from the patch file. If no hunk can be found, set *HUNK to NULL. If we
- * have a property hunk, PROP_NAME will be set. If we have a text hunk,
- * PROP_NAME will be NULL. If REVERSE is TRUE, invert the hunk while
- * parsing it. If IGNORE_WHiTESPACES is TRUE, let lines without leading
- * spaces be recognized as context lines. Allocate results in RESULT_POOL.
- * Use SCRATCH_POOL for all other allocations. */
+ * from the patch file. If no hunk can be found, set *HUNK to NULL. Set
+ * IS_PROPERTY to TRUE if we have a property hunk. If the returned HUNK is
+ * the first belonging to a certain property, then PROP_NAME and
+ * PROP_OPERATION will be set too. If we have a text hunk, PROP_NAME will be
+ * NULL. If IGNORE_WHITESPACE is TRUE, let lines without leading spaces be
+ * recognized as context lines. Allocate results in
+ * RESULT_POOL. Use SCRATCH_POOL for all other allocations. */
static svn_error_t *
-parse_next_hunk(svn_hunk_t **hunk,
+parse_next_hunk(svn_diff_hunk_t **hunk,
+ svn_boolean_t *is_property,
const char **prop_name,
+ svn_diff_operation_kind_t *prop_operation,
svn_patch_t *patch,
svn_stream_t *stream,
- svn_boolean_t reverse,
svn_boolean_t ignore_whitespace,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -316,11 +531,11 @@ parse_next_hunk(svn_hunk_t **hunk,
svn_boolean_t changed_line_seen;
apr_pool_t *iterpool;
- /* We only set this if we have a property hunk.
- * ### prop_name acts as both a state flag inside this function and a
- * ### qualifier to discriminate between props and text hunks. Is that
- * ### kind of overloading ok? */
+ *prop_operation = svn_diff_op_unchanged;
+
+ /* We only set this if we have a property hunk header. */
*prop_name = NULL;
+ *is_property = FALSE;
if (apr_file_eof(patch->patch_file) == APR_EOF)
{
@@ -367,19 +582,8 @@ parse_next_hunk(svn_hunk_t **hunk,
if (in_hunk)
{
char c;
- char add;
- char del;
-
- if (reverse)
- {
- add = '-';
- del = '+';
- }
- else
- {
- add = '+';
- del = '-';
- }
+ static const char add = '+';
+ static const char del = '-';
if (! hunk_seen)
{
@@ -440,46 +644,54 @@ parse_next_hunk(svn_hunk_t **hunk,
}
else
{
+ /* ### Add an is_hunk_header() helper function that returns
+ * ### the proper atat string? Then we could collapse the
+ * ### following two if-clauses. */
if (starts_with(line->data, text_atat))
{
/* Looks like we have a hunk header, try to rip it apart. */
in_hunk = parse_hunk_header(line->data, *hunk, text_atat,
- reverse, iterpool);
+ iterpool);
if (in_hunk)
{
original_lines = (*hunk)->original_length;
modified_lines = (*hunk)->modified_length;
- *prop_name = NULL;
+ *is_property = FALSE;
}
}
- else if (starts_with(line->data, prop_atat) && *prop_name)
+ else if (starts_with(line->data, prop_atat))
{
/* Looks like we have a property hunk header, try to rip it
* apart. */
in_hunk = parse_hunk_header(line->data, *hunk, prop_atat,
- reverse, iterpool);
+ iterpool);
if (in_hunk)
{
original_lines = (*hunk)->original_length;
modified_lines = (*hunk)->modified_length;
+ *is_property = TRUE;
}
}
else if (starts_with(line->data, "Added: "))
{
SVN_ERR(parse_prop_name(prop_name, line->data, "Added: ",
result_pool));
+ *prop_operation = svn_diff_op_added;
}
else if (starts_with(line->data, "Deleted: "))
{
SVN_ERR(parse_prop_name(prop_name, line->data, "Deleted: ",
result_pool));
+ *prop_operation = svn_diff_op_deleted;
}
else if (starts_with(line->data, "Modified: "))
{
SVN_ERR(parse_prop_name(prop_name, line->data, "Modified: ",
result_pool));
+ *prop_operation = svn_diff_op_modified;
}
- else if (starts_with(line->data, minus))
+ else if (starts_with(line->data, minus)
+ || starts_with(line->data, "diff --git "))
/* This could be a header of another patch. Bail out. */
break;
}
@@ -514,9 +726,6 @@ parse_next_hunk(svn_hunk_t **hunk,
original_text = svn_stream_from_aprfile_range_readonly(f, FALSE,
start, end,
result_pool);
- svn_stream_set_line_filter_callback(original_text, original_line_filter);
- svn_stream_set_line_transformer_callback(original_text,
- remove_leading_char_transformer);
/* Create a stream which returns the modified hunk text. */
SVN_ERR(svn_io_file_open(&f, patch->path, flags, APR_OS_DEFAULT,
@@ -524,23 +733,11 @@ parse_next_hunk(svn_hunk_t **hunk,
modified_text = svn_stream_from_aprfile_range_readonly(f, FALSE,
start, end,
result_pool);
- svn_stream_set_line_filter_callback(modified_text, modified_line_filter);
- svn_stream_set_line_transformer_callback(modified_text,
- remove_leading_char_transformer);
- /* Set the hunk's texts. */
+
(*hunk)->diff_text = diff_text;
- if (reverse)
- {
- svn_stream_set_line_transformer_callback(diff_text,
- reverse_diff_transformer);
- (*hunk)->original_text = modified_text;
- (*hunk)->modified_text = original_text;
- }
- else
- {
- (*hunk)->original_text = original_text;
- (*hunk)->modified_text = modified_text;
- }
+ (*hunk)->patch = patch;
+ (*hunk)->original_text = original_text;
+ (*hunk)->modified_text = modified_text;
(*hunk)->leading_context = leading_context;
(*hunk)->trailing_context = trailing_context;
}
@@ -556,8 +753,8 @@ parse_next_hunk(svn_hunk_t **hunk,
static int
compare_hunks(const void *a, const void *b)
{
- const svn_hunk_t *ha = *((const svn_hunk_t **)a);
- const svn_hunk_t *hb = *((const svn_hunk_t **)b);
+ const svn_diff_hunk_t *ha = *((const svn_diff_hunk_t **)a);
+ const svn_diff_hunk_t *hb = *((const svn_diff_hunk_t **)b);
if (ha->original_start < hb->original_start)
return -1;
@@ -570,7 +767,7 @@ compare_hunks(const void *a, const void
* Ensure that all streams which were opened for HUNK are closed.
*/
static svn_error_t *
-close_hunk(const svn_hunk_t *hunk)
+close_hunk(const svn_diff_hunk_t *hunk)
{
SVN_ERR(svn_stream_close(hunk->original_text));
SVN_ERR(svn_stream_close(hunk->modified_text));
@@ -578,6 +775,353 @@ close_hunk(const svn_hunk_t *hunk)
return SVN_NO_ERROR;
}
+/* Possible states of the diff header parser. */
+enum parse_state
+{
+ state_start, /* initial */
+ state_git_diff_seen, /* diff --git */
+ state_git_tree_seen, /* a tree operation, rather then content change */
+ state_git_minus_seen, /* --- /dev/null; or --- a/ */
+ state_git_plus_seen, /* +++ /dev/null; or +++ a/ */
+ state_move_from_seen, /* rename from foo.c */
+ state_copy_from_seen, /* copy from foo.c */
+ state_minus_seen, /* --- foo.c */
+ state_unidiff_found, /* valid start of a regular unidiff header */
+ state_add_seen, /* ### unused? */
+ state_del_seen, /* ### unused? */
+ state_git_header_found /* valid start of a --git diff header */
+};
+
+/* Data type describing a valid state transition of the parser. */
+struct transition
+{
+ const char *expected_input;
+ enum parse_state required_state;
+
+ /* A callback called upon each parser state transition. */
+ svn_error_t *(*fn)(enum parse_state *new_state, const char *input,
+ svn_patch_t *patch, apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+};
+
+/* UTF-8 encode and canonicalize the content of LINE as FILE_NAME. */
+static svn_error_t *
+grab_filename(const char **file_name, const char *line, apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *utf8_path;
+ const char *canon_path;
+
+ /* Grab the filename and encode it in UTF-8. */
+ /* TODO: Allow specifying the patch file's encoding.
+ * For now, we assume its encoding is native. */
+ /* ### This can fail if the filename cannot be represented in the current
+ * ### locale's encoding. */
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_path,
+ line,
+ scratch_pool));
+
+ /* Canonicalize the path name. */
+ canon_path = svn_dirent_canonicalize(utf8_path, scratch_pool);
+
+ *file_name = apr_pstrdup(result_pool, canon_path);
+
+ return SVN_NO_ERROR;
+}
+
+/* Parse the '--- ' line of a regular unidiff. */
+static svn_error_t *
+diff_minus(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ /* If we can find a tab, it separates the filename from
+ * the rest of the line which we can discard. */
+ char *tab = strchr(line, '\t');
+ if (tab)
+ *tab = '\0';
+
+ SVN_ERR(grab_filename(&patch->old_filename, line + strlen("--- "),
+ result_pool, scratch_pool));
+
+ *new_state = state_minus_seen;
+
+ return SVN_NO_ERROR;
+}
+
+/* Parse the '+++ ' line of a regular unidiff. */
+static svn_error_t *
+diff_plus(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ /* If we can find a tab, it separates the filename from
+ * the rest of the line which we can discard. */
+ char *tab = strchr(line, '\t');
+ if (tab)
+ *tab = '\0';
+
+ SVN_ERR(grab_filename(&patch->new_filename, line + strlen("+++ "),
+ result_pool, scratch_pool));
+
+ *new_state = state_unidiff_found;
+
+ return SVN_NO_ERROR;
+}
+
+/* Parse the first line of a git extended unidiff. */
+static svn_error_t *
+git_start(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ const char *old_path_start;
+ char *old_path_end;
+ const char *new_path_start;
+ const char *new_path_end;
+ char *new_path_marker;
+ const char *old_path_marker;
+
+ /* ### Add handling of escaped paths
+ * http://www.kernel.org/pub/software/scm/git/docs/git-diff.html:
+ *
+ * TAB, LF, double quote and backslash characters in pathnames are
+ * represented as \t, \n, \" and \\, respectively. If there is need for
+ * such substitution then the whole pathname is put in double quotes.
+ */
+
+ /* Our line should look like this: 'diff --git a/path b/path'.
+ *
+ * If we find any deviations from that format, we return with state reset
+ * to start.
+ */
+ old_path_marker = strstr(line, " a/");
+
+ if (! old_path_marker)
+ {
+ *new_state = state_start;
+ return SVN_NO_ERROR;
+ }
+
+ if (! *(old_path_marker + 3))
+ {
+ *new_state = state_start;
+ return SVN_NO_ERROR;
+ }
+
+ new_path_marker = strstr(old_path_marker, " b/");
+
+ if (! new_path_marker)
+ {
+ *new_state = state_start;
+ return SVN_NO_ERROR;
+ }
+
+ if (! *(new_path_marker + 3))
+ {
+ *new_state = state_start;
+ return SVN_NO_ERROR;
+ }
+
+ /* By now, we know that we have a line on the form '--git diff a/.+ b/.+'
+ * We only need the filenames when we have deleted or added empty
+ * files. In those cases the old_path and new_path is identical on the
+ * 'diff --git' line. For all other cases we fetch the filenames from
+ * other header lines. */
+ old_path_start = line + strlen("diff --git a/");
+ new_path_end = line + strlen(line);
+ new_path_start = old_path_start;
+
+ while (TRUE)
+ {
+ int len_old;
+ int len_new;
+
+ new_path_marker = strstr(new_path_start, " b/");
+
+ /* No new path marker, bail out. */
+ if (! new_path_marker)
+ break;
+
+ old_path_end = new_path_marker;
+ new_path_start = new_path_marker + strlen(" b/");
+
+ /* No path after the marker. */
+ if (! *new_path_start)
+ break;
+
+ len_old = old_path_end - old_path_start;
+ len_new = new_path_end - new_path_start;
+
+ /* Are the paths before and after the " b/" marker the same? */
+ if (len_old == len_new
+ && ! strncmp(old_path_start, new_path_start, len_old))
+ {
+ *old_path_end = '\0';
+ SVN_ERR(grab_filename(&patch->old_filename, old_path_start,
+ result_pool, scratch_pool));
+
+ SVN_ERR(grab_filename(&patch->new_filename, new_path_start,
+ result_pool, scratch_pool));
+ break;
+ }
+ }
+
+ /* We assume that the path is only modified until we've found a 'tree'
+ * header */
+ patch->operation = svn_diff_op_modified;
+
+ *new_state = state_git_diff_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the '--- ' line of a git extended unidiff. */
+static svn_error_t *
+git_minus(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ /* If we can find a tab, it separates the filename from
+ * the rest of the line which we can discard. */
+ char *tab = strchr(line, '\t');
+ if (tab)
+ *tab = '\0';
+
+ if (starts_with(line, "--- /dev/null"))
+ SVN_ERR(grab_filename(&patch->old_filename, "/dev/null",
+ result_pool, scratch_pool));
+ else
+ SVN_ERR(grab_filename(&patch->old_filename, line + strlen("--- a/"),
+ result_pool, scratch_pool));
+
+ *new_state = state_git_minus_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the '+++ ' line of a git extended unidiff. */
+static svn_error_t *
+git_plus(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ /* If we can find a tab, it separates the filename from
+ * the rest of the line which we can discard. */
+ char *tab = strchr(line, '\t');
+ if (tab)
+ *tab = '\0'; /* ### indirectly modifying LINE, which is const */
+
+ if (starts_with(line, "+++ /dev/null"))
+ SVN_ERR(grab_filename(&patch->new_filename, "/dev/null",
+ result_pool, scratch_pool));
+ else
+ SVN_ERR(grab_filename(&patch->new_filename, line + strlen("+++ b/"),
+ result_pool, scratch_pool));
+
+ *new_state = state_git_header_found;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the 'rename from ' line of a git extended unidiff. */
+static svn_error_t *
+git_move_from(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ SVN_ERR(grab_filename(&patch->old_filename, line + strlen("rename from "),
+ result_pool, scratch_pool));
+
+ *new_state = state_move_from_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the 'rename to ' line fo a git extended unidiff. */
+static svn_error_t *
+git_move_to(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ SVN_ERR(grab_filename(&patch->new_filename, line + strlen("rename to "),
+ result_pool, scratch_pool));
+
+ patch->operation = svn_diff_op_moved;
+
+ *new_state = state_git_tree_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the 'copy from ' line of a git extended unidiff. */
+static svn_error_t *
+git_copy_from(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ SVN_ERR(grab_filename(&patch->old_filename, line + strlen("copy from "),
+ result_pool, scratch_pool));
+
+ *new_state = state_copy_from_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the 'copy to ' line of a git extended unidiff. */
+static svn_error_t *
+git_copy_to(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ SVN_ERR(grab_filename(&patch->new_filename, line + strlen("copy to "),
+ result_pool, scratch_pool));
+
+ patch->operation = svn_diff_op_copied;
+
+ *new_state = state_git_tree_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the 'new file ' line of a git extended unidiff. */
+static svn_error_t *
+git_new_file(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ patch->operation = svn_diff_op_added;
+
+ /* Filename already retrieved from diff --git header. */
+
+ *new_state = state_git_tree_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Parse the 'deleted file ' line of a git extended unidiff. */
+static svn_error_t *
+git_deleted_file(enum parse_state *new_state, const char *line, svn_patch_t *patch,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ patch->operation = svn_diff_op_deleted;
+
+ /* Filename already retrieved from diff --git header. */
+
+ *new_state = state_git_tree_seen;
+ return SVN_NO_ERROR;
+}
+
+/* Add a HUNK associated with the property PROP_NAME to PATCH. */
+static svn_error_t *
+add_property_hunk(svn_patch_t *patch, const char *prop_name,
+ svn_diff_hunk_t *hunk, svn_diff_operation_kind_t operation,
+ apr_pool_t *result_pool)
+{
+ svn_prop_patch_t *prop_patch;
+
+ prop_patch = apr_hash_get(patch->prop_patches, prop_name,
+ APR_HASH_KEY_STRING);
+
+ if (! prop_patch)
+ {
+ prop_patch = apr_palloc(result_pool, sizeof(svn_prop_patch_t));
+ prop_patch->name = prop_name;
+ prop_patch->operation = operation;
+ prop_patch->hunks = apr_array_make(result_pool, 1,
+ sizeof(svn_diff_hunk_t *));
+
+ apr_hash_set(patch->prop_patches, prop_name, APR_HASH_KEY_STRING,
+ prop_patch);
+ }
+
+ APR_ARRAY_PUSH(prop_patch->hunks, svn_diff_hunk_t *) = hunk;
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
svn_diff_parse_next_patch(svn_patch_t **patch,
apr_file_t *patch_file,
@@ -586,17 +1130,36 @@ svn_diff_parse_next_patch(svn_patch_t **
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- static const char * const minus = "--- ";
- static const char * const plus = "+++ ";
- const char *indicator;
const char *fname;
svn_stream_t *stream;
- svn_boolean_t eof, in_header;
- svn_hunk_t *hunk;
- const char *prop_name;
+ apr_off_t pos, last_line;
+ svn_boolean_t eof;
+ svn_boolean_t line_after_tree_header_read = FALSE;
apr_pool_t *iterpool;
+ enum parse_state state = state_start;
+
+ /* Our table consisting of:
+ * Expected Input Required state Function to call */
+ struct transition transitions[] =
+ {
+ {"--- ", state_start, diff_minus},
+ {"+++ ", state_minus_seen, diff_plus},
+ {"diff --git", state_start, git_start},
+ {"--- a/", state_git_diff_seen, git_minus},
+ {"--- a/", state_git_tree_seen, git_minus},
+ {"--- /dev/null", state_git_tree_seen, git_minus},
+ {"+++ b/", state_git_minus_seen, git_plus},
+ {"+++ /dev/null", state_git_minus_seen, git_plus},
+ {"rename from ", state_git_diff_seen, git_move_from},
+ {"rename to ", state_move_from_seen, git_move_to},
+ {"copy from ", state_git_diff_seen, git_copy_from},
+ {"copy to ", state_copy_from_seen, git_copy_to},
+ {"new file ", state_git_diff_seen, git_new_file},
+ {"deleted file ", state_git_diff_seen, git_deleted_file},
+ };
+
if (apr_file_eof(patch_file) == APR_EOF)
{
/* No more patches here. */
@@ -617,66 +1180,75 @@ svn_diff_parse_next_patch(svn_patch_t **
* make sure it is disowned. */
stream = svn_stream_from_aprfile2(patch_file, TRUE, scratch_pool);
- indicator = minus;
- in_header = FALSE;
+ /* Get current seek position -- APR has no ftell() :( */
+ pos = 0;
+ SVN_ERR(svn_io_file_seek((*patch)->patch_file, APR_CUR, &pos, scratch_pool));
+
iterpool = svn_pool_create(scratch_pool);
+
do
{
svn_stringbuf_t *line;
+ int i;
svn_pool_clear(iterpool);
- /* Read a line from the stream. */
+ /* Remember the current line's offset, and read the line. */
+ last_line = pos;
SVN_ERR(svn_stream_readline_detect_eol(stream, &line, NULL, &eof,
iterpool));
- /* See if we have a diff header. */
- if (line->len > strlen(indicator) && starts_with(line->data, indicator))
+ if (! eof)
{
- const char *utf8_path;
- const char *canon_path;
+ /* Update line offset for next iteration.
+ * APR has no ftell() :( */
+ pos = 0;
+ SVN_ERR(svn_io_file_seek((*patch)->patch_file, APR_CUR, &pos, iterpool));
+ }
- /* If we can find a tab, it separates the filename from
- * the rest of the line which we can discard. */
- char *tab = strchr(line->data, '\t');
- if (tab)
- *tab = '\0';
-
- /* Grab the filename and encode it in UTF-8. */
- /* TODO: Allow specifying the patch file's encoding.
- * For now, we assume its encoding is native. */
- SVN_ERR(svn_utf_cstring_to_utf8(&utf8_path,
- line->data + strlen(indicator),
- iterpool));
-
- /* Canonicalize the path name. */
- canon_path = svn_dirent_canonicalize(utf8_path, iterpool);
-
- if ((! in_header) && strcmp(indicator, minus) == 0)
- {
- /* First line of header contains old filename. */
- if (reverse)
- (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
- else
- (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
- indicator = plus;
- in_header = TRUE;
- }
- else if (in_header && strcmp(indicator, plus) == 0)
- {
- /* Second line of header contains new filename. */
- if (reverse)
- (*patch)->old_filename = apr_pstrdup(result_pool, canon_path);
- else
- (*patch)->new_filename = apr_pstrdup(result_pool, canon_path);
- in_header = FALSE;
- break; /* All good! */
+ /* Run the state machine. */
+ for (i = 0; i < (sizeof(transitions) / sizeof(transitions[0])); i++)
+ {
+ if (line->len > strlen(transitions[i].expected_input)
+ && starts_with(line->data, transitions[i].expected_input)
+ && state == transitions[i].required_state)
+ {
+ SVN_ERR(transitions[i].fn(&state, line->data, *patch,
+ result_pool, iterpool));
+ break;
}
- else
- in_header = FALSE;
}
+
+ if (state == state_unidiff_found
+ || state == state_git_header_found)
+ {
+ /* We have a valid diff header, yay! */
+ break;
+ }
+ else if (state == state_git_tree_seen
+ && line_after_tree_header_read)
+ {
+ /* We have a valid diff header for a patch with only tree changes.
+ * Rewind to the start of the line just read, so subsequent calls
+ * to this function don't end up skipping the line -- it may
+ * contain a patch. */
+ SVN_ERR(svn_io_file_seek((*patch)->patch_file, APR_SET, &last_line,
+ scratch_pool));
+ break;
+ }
+ else if (state == state_git_tree_seen)
+ line_after_tree_header_read = TRUE;
+
+ } while (! eof);
+
+ (*patch)->reverse = reverse;
+ if (reverse)
+ {
+ const char *temp;
+ temp = (*patch)->old_filename;
+ (*patch)->old_filename = (*patch)->new_filename;
+ (*patch)->new_filename = temp;
}
- while (! eof);
if ((*patch)->old_filename == NULL || (*patch)->new_filename == NULL)
{
@@ -685,34 +1257,39 @@ svn_diff_parse_next_patch(svn_patch_t **
}
else
{
+ svn_diff_hunk_t *hunk;
+ svn_boolean_t is_property;
+ const char *last_prop_name;
+ const char *prop_name;
+ svn_diff_operation_kind_t prop_operation;
+
/* Parse hunks. */
- (*patch)->hunks = apr_array_make(result_pool, 10, sizeof(svn_hunk_t *));
- (*patch)->property_hunks = apr_hash_make(result_pool);
+ (*patch)->hunks = apr_array_make(result_pool, 10,
+ sizeof(svn_diff_hunk_t *));
+ (*patch)->prop_patches = apr_hash_make(result_pool);
do
{
svn_pool_clear(iterpool);
- SVN_ERR(parse_next_hunk(&hunk, &prop_name, *patch, stream,
- reverse, ignore_whitespace,
+ SVN_ERR(parse_next_hunk(&hunk, &is_property, &prop_name,
+ &prop_operation, *patch, stream,
+ ignore_whitespace,
result_pool, iterpool));
- if (hunk && prop_name)
- {
- apr_array_header_t *hunks;
- hunks = apr_hash_get((*patch)->property_hunks, prop_name,
- APR_HASH_KEY_STRING);
- if (! hunks)
- {
- hunks = apr_array_make(result_pool, 1,
- sizeof(svn_hunk_t *));
- apr_hash_set((*patch)->property_hunks, prop_name,
- APR_HASH_KEY_STRING, hunks);
- }
-
- APR_ARRAY_PUSH(hunks, svn_hunk_t *) = hunk;
+ if (hunk && is_property)
+ {
+ if (! prop_name)
+ prop_name = last_prop_name;
+ else
+ last_prop_name = prop_name;
+ SVN_ERR(add_property_hunk(*patch, prop_name, hunk, prop_operation,
+ result_pool));
}
else if (hunk)
- APR_ARRAY_PUSH((*patch)->hunks, svn_hunk_t *) = hunk;
+ {
+ APR_ARRAY_PUSH((*patch)->hunks, svn_diff_hunk_t *) = hunk;
+ last_prop_name = NULL;
+ }
}
while (hunk);
@@ -735,15 +1312,34 @@ svn_diff_parse_next_patch(svn_patch_t **
}
svn_error_t *
-svn_diff_close_patch(const svn_patch_t *patch)
+svn_diff_close_patch(const svn_patch_t *patch, apr_pool_t *scratch_pool)
{
int i;
+ apr_hash_index_t *hi;
for (i = 0; i < patch->hunks->nelts; i++)
{
- const svn_hunk_t *hunk = APR_ARRAY_IDX(patch->hunks, i, svn_hunk_t *);
+ const svn_diff_hunk_t *hunk = APR_ARRAY_IDX(patch->hunks, i,
+ svn_diff_hunk_t *);
SVN_ERR(close_hunk(hunk));
}
+ for (hi = apr_hash_first(scratch_pool, patch->prop_patches);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ svn_prop_patch_t *prop_patch;
+
+ prop_patch = svn__apr_hash_index_val(hi);
+
+ for (i = 0; i < prop_patch->hunks->nelts; i++)
+ {
+ const svn_diff_hunk_t *hunk;
+
+ hunk = APR_ARRAY_IDX(prop_patch->hunks, i, svn_diff_hunk_t *);
+ SVN_ERR(close_hunk(hunk));
+ }
+ }
+
return SVN_NO_ERROR;
}
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.c Wed Sep 15 19:32:26 2010
@@ -1301,16 +1301,29 @@ svn_fs_get_lock(svn_lock_t **lock, svn_f
}
svn_error_t *
+svn_fs_get_locks2(svn_fs_t *fs, const char *path, svn_depth_t depth,
+ svn_fs_get_locks_callback_t get_locks_func,
+ void *get_locks_baton, apr_pool_t *pool)
+{
+ SVN_ERR_ASSERT((depth == svn_depth_empty) ||
+ (depth == svn_depth_files) ||
+ (depth == svn_depth_immediates) ||
+ (depth == svn_depth_infinity));
+ return svn_error_return(fs->vtable->get_locks(fs, path, depth,
+ get_locks_func,
+ get_locks_baton, pool));
+}
+
+svn_error_t *
svn_fs_get_locks(svn_fs_t *fs, const char *path,
svn_fs_get_locks_callback_t get_locks_func,
- void *get_locks_baton,
- apr_pool_t *pool)
+ void *get_locks_baton, apr_pool_t *pool)
{
- return svn_error_return(fs->vtable->get_locks(fs, path, get_locks_func,
- get_locks_baton, pool));
+ return svn_error_return(svn_fs_get_locks2(fs, path, svn_depth_infinity,
+ get_locks_func, get_locks_baton,
+ pool));
}
-
/* --- History functions --- */
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs/fs-loader.h Wed Sep 15 19:32:26 2010
@@ -190,7 +190,7 @@ typedef struct fs_vtable_t
svn_boolean_t break_lock, apr_pool_t *pool);
svn_error_t *(*get_lock)(svn_lock_t **lock, svn_fs_t *fs,
const char *path, apr_pool_t *pool);
- svn_error_t *(*get_locks)(svn_fs_t *fs, const char *path,
+ svn_error_t *(*get_locks)(svn_fs_t *fs, const char *path, svn_depth_t depth,
svn_fs_get_locks_callback_t get_locks_func,
void *get_locks_baton,
apr_pool_t *pool);
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/lock-tokens-table.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/lock-tokens-table.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/lock-tokens-table.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/lock-tokens-table.c Wed Sep 15 19:32:26 2010
@@ -104,7 +104,7 @@ svn_fs_bdb__lock_token_delete(svn_fs_t *
svn_fs_base__trail_debug(trail, "lock-tokens", "del");
db_err = bfd->lock_tokens->del(bfd->lock_tokens, trail->db_txn, &key, 0);
if (db_err == DB_NOTFOUND)
- return SVN_FS__ERR_NO_SUCH_LOCK(fs, path);
+ return SVN_FS__ERR_NO_SUCH_LOCK(fs, path, pool);
return BDB_WRAP(fs, "deleting entry from 'lock-tokens' table", db_err);
}
@@ -131,7 +131,7 @@ svn_fs_bdb__lock_token_get(const char **
svn_fs_base__track_dbt(&value, pool);
if (db_err == DB_NOTFOUND)
- return SVN_FS__ERR_NO_SUCH_LOCK(fs, path);
+ return SVN_FS__ERR_NO_SUCH_LOCK(fs, path, pool);
SVN_ERR(BDB_WRAP(fs, "reading lock token", db_err));
lock_token = apr_pstrmemdup(pool, value.data, value.size);
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.c Wed Sep 15 19:32:26 2010
@@ -152,7 +152,7 @@ svn_fs_bdb__lock_get(svn_lock_t **lock_p
if (lock->expiration_date && (apr_time_now() > lock->expiration_date))
{
SVN_ERR(svn_fs_bdb__lock_delete(fs, lock_token, trail, pool));
- return SVN_FS__ERR_LOCK_EXPIRED(fs, lock_token);
+ return SVN_FS__ERR_LOCK_EXPIRED(fs, lock_token, pool);
}
*lock_p = lock;
@@ -191,6 +191,7 @@ get_lock(svn_lock_t **lock_p,
svn_error_t *
svn_fs_bdb__locks_get(svn_fs_t *fs,
const char *path,
+ svn_depth_t depth,
svn_fs_get_locks_callback_t get_locks_func,
void *get_locks_baton,
trail_t *trail,
@@ -225,9 +226,11 @@ svn_fs_bdb__locks_get(svn_fs_t *fs,
SVN_ERR(get_locks_func(get_locks_baton, lock, pool));
}
+ /* If we're only looking at PATH itself (depth = empty), stop here. */
+ if (depth == svn_depth_empty)
+ return SVN_NO_ERROR;
+
/* Now go hunt for possible children of PATH. */
- if (strcmp(path, "/") != 0)
- lookup_path = apr_pstrcat(pool, path, "/", NULL);
svn_fs_base__trail_debug(trail, "lock-tokens", "cursor");
db_err = bfd->lock_tokens->cursor(bfd->lock_tokens, trail->db_txn,
@@ -246,6 +249,8 @@ svn_fs_bdb__locks_get(svn_fs_t *fs,
/* As long as the prefix of the returned KEY matches LOOKUP_PATH we
know it is either LOOKUP_PATH or a decendant thereof. */
+ if (strcmp(path, "/") != 0)
+ lookup_path = apr_pstrcat(pool, path, "/", NULL);
while ((! db_err)
&& strncmp(lookup_path, key.data, strlen(lookup_path)) == 0)
{
@@ -260,6 +265,18 @@ svn_fs_bdb__locks_get(svn_fs_t *fs,
child_path = apr_pstrmemdup(subpool, key.data, key.size);
lock_token = apr_pstrmemdup(subpool, value.data, value.size);
+ if ((depth == svn_depth_files) || (depth == svn_depth_immediates))
+ {
+ /* On the assumption that we only store locks for files,
+ depth=files and depth=immediates should boil down to the
+ same set of results. So just see if CHILD_PATH is an
+ immediate child of PATH. If not, we don't care about
+ this item. */
+ const char *rel_uri = svn_uri_is_child(path, child_path, subpool);
+ if (!rel_uri || (svn_path_component_count(rel_uri) != 1))
+ goto loop_it;
+ }
+
/* Get the lock for CHILD_PATH. */
err = get_lock(&lock, fs, child_path, lock_token, trail, subpool);
if (err)
@@ -279,6 +296,7 @@ svn_fs_bdb__locks_get(svn_fs_t *fs,
}
}
+ loop_it:
svn_fs_base__result_dbt(&key);
svn_fs_base__result_dbt(&value);
db_err = svn_bdb_dbc_get(cursor, &key, &value, DB_NEXT);
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/bdb/locks-table.h Wed Sep 15 19:32:26 2010
@@ -86,12 +86,16 @@ svn_error_t *svn_fs_bdb__lock_get(svn_lo
in FS. Pass each lock to GET_LOCKS_FUNC callback along with
GET_LOCKS_BATON.
+ Use DEPTH to filter the reported locks to only those within the
+ requested depth of PATH.
+
This function promises to auto-expire any locks encountered while
building the hash. That means that the caller can trust that each
returned lock hasn't yet expired.
*/
svn_error_t *svn_fs_bdb__locks_get(svn_fs_t *fs,
const char *path,
+ svn_depth_t depth,
svn_fs_get_locks_callback_t get_locks_func,
void *get_locks_baton,
trail_t *trail,
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.c Wed Sep 15 19:32:26 2010
@@ -92,7 +92,7 @@ txn_body_lock(void *baton, trail_t *trai
/* Until we implement directory locks someday, we only allow locks
on files or non-existent paths. */
if (kind == svn_node_dir)
- return SVN_FS__ERR_NOT_FILE(trail->fs, args->path);
+ return SVN_FS__ERR_NOT_FILE(trail->fs, args->path, trail->pool);
/* While our locking implementation easily supports the locking of
nonexistent paths, we deliberately choose not to allow such madness. */
@@ -112,7 +112,7 @@ txn_body_lock(void *baton, trail_t *trai
/* There better be a username attached to the fs. */
if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
- return SVN_FS__ERR_NO_USER(trail->fs);
+ return SVN_FS__ERR_NO_USER(trail->fs, trail->pool);
/* Is the caller attempting to lock an out-of-date working file? */
if (SVN_IS_VALID_REVNUM(args->current_rev))
@@ -178,7 +178,8 @@ txn_body_lock(void *baton, trail_t *trai
{
/* Sorry, the path is already locked. */
return SVN_FS__ERR_PATH_ALREADY_LOCKED(trail->fs,
- existing_lock);
+ existing_lock,
+ trail->pool);
}
else
{
@@ -280,21 +281,22 @@ txn_body_unlock(void *baton, trail_t *tr
if (args->token == NULL)
return svn_fs_base__err_no_lock_token(trail->fs, args->path);
else if (strcmp(lock_token, args->token) != 0)
- return SVN_FS__ERR_NO_SUCH_LOCK(trail->fs, args->path);
+ return SVN_FS__ERR_NO_SUCH_LOCK(trail->fs, args->path, trail->pool);
SVN_ERR(svn_fs_bdb__lock_get(&lock, trail->fs, lock_token,
trail, trail->pool));
/* There better be a username attached to the fs. */
if (!trail->fs->access_ctx || !trail->fs->access_ctx->username)
- return SVN_FS__ERR_NO_USER(trail->fs);
+ return SVN_FS__ERR_NO_USER(trail->fs, trail->pool);
/* And that username better be the same as the lock's owner. */
if (strcmp(trail->fs->access_ctx->username, lock->owner) != 0)
- return SVN_FS__ERR_LOCK_OWNER_MISMATCH
- (trail->fs,
+ return SVN_FS__ERR_LOCK_OWNER_MISMATCH(
+ trail->fs,
trail->fs->access_ctx->username,
- lock->owner);
+ lock->owner,
+ trail->pool);
}
/* Remove a row from each of the locking tables. */
@@ -396,6 +398,7 @@ svn_fs_base__get_lock(svn_lock_t **lock,
struct locks_get_args
{
const char *path;
+ svn_depth_t depth;
svn_fs_get_locks_callback_t get_locks_func;
void *get_locks_baton;
};
@@ -405,7 +408,7 @@ static svn_error_t *
txn_body_get_locks(void *baton, trail_t *trail)
{
struct locks_get_args *args = baton;
- return svn_fs_bdb__locks_get(trail->fs, args->path,
+ return svn_fs_bdb__locks_get(trail->fs, args->path, args->depth,
args->get_locks_func, args->get_locks_baton,
trail, trail->pool);
}
@@ -414,6 +417,7 @@ txn_body_get_locks(void *baton, trail_t
svn_error_t *
svn_fs_base__get_locks(svn_fs_t *fs,
const char *path,
+ svn_depth_t depth,
svn_fs_get_locks_callback_t get_locks_func,
void *get_locks_baton,
apr_pool_t *pool)
@@ -422,6 +426,7 @@ svn_fs_base__get_locks(svn_fs_t *fs,
SVN_ERR(svn_fs__check_fs(fs, TRUE));
args.path = svn_fs__canonicalize_abspath(path, pool);
+ args.depth = depth;
args.get_locks_func = get_locks_func;
args.get_locks_baton = get_locks_baton;
return svn_fs_base__retry_txn(fs, txn_body_get_locks, &args, FALSE, pool);
@@ -490,7 +495,8 @@ svn_fs_base__allow_locked_operation(cons
if (recurse)
{
/* Discover all locks at or below the path. */
- SVN_ERR(svn_fs_bdb__locks_get(trail->fs, path, get_locks_callback,
+ SVN_ERR(svn_fs_bdb__locks_get(trail->fs, path, svn_depth_infinity,
+ get_locks_callback,
trail->fs, trail, pool));
}
else
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/lock.h Wed Sep 15 19:32:26 2010
@@ -64,6 +64,7 @@ svn_error_t *svn_fs_base__get_lock(svn_l
svn_error_t *
svn_fs_base__get_locks(svn_fs_t *fs,
const char *path,
+ svn_depth_t depth,
svn_fs_get_locks_callback_t get_locks_func,
void *get_locks_baton,
apr_pool_t *pool);
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/fs-history
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/fs-history?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/fs-history (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/fs-history Wed Sep 15 19:32:26 2010
@@ -63,7 +63,7 @@ under-the-hood is as follows:
1. The root refers to a particular snapshot of the DAG node tree,
and from this we can learn the node-revision ID of the node
which represents the root directory ("/") as it appears in that
- snaphot. Given this node-revision ID, it's all DAG from here.
+ snapshot. Given this node-revision ID, it's all DAG from here.
2. The path is split into components and traversed, beginning with
the root node, and walking down toward the full path. Each
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/structure
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/structure?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/structure (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/notes/structure Wed Sep 15 19:32:26 2010
@@ -141,7 +141,7 @@ Note that a node cannot change its kind
A directory node is always a directory; a file node is always a file;
etc. The fact that the node's kind is stored in each node revision,
rather than in some revision-independent place, might suggest that
-it's possible for a node change kinds from revision to revision, but
+it's possible for a node to change kinds from revision to revision, but
Subversion does not allow this.
PROP-KEY is a key into the `representations' table (see REPRESENTATIONS
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/tree.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/tree.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/tree.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/tree.c Wed Sep 15 19:32:26 2010
@@ -430,7 +430,7 @@ mutable_root_node(dag_node_t **node_p,
trail, pool);
else
/* If it's not a transaction root, we can't change its contents. */
- return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path);
+ return SVN_FS__ERR_NOT_MUTABLE(root->fs, root->rev, error_path, pool);
}
@@ -768,7 +768,7 @@ open_path(parent_path_t **parent_path_p,
/* The path isn't finished yet; we'd better be in a directory. */
if (svn_fs_base__dag_node_kind(child) != svn_node_dir)
- SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far),
+ SVN_ERR_W(SVN_FS__ERR_NOT_DIRECTORY(fs, path_so_far, pool),
apr_psprintf(pool, _("Failure opening '%s'"), path));
rest = next;
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/util/fs_skels.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/util/fs_skels.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/util/fs_skels.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_base/util/fs_skels.c Wed Sep 15 19:32:26 2010
@@ -31,6 +31,7 @@
#include "svn_time.h"
#include "private/svn_skel.h"
+#include "private/svn_dep_compat.h"
#include "svn_checksum.h"
#include "fs_skels.h"
@@ -550,32 +551,38 @@ svn_fs_base__parse_representation_skel(r
{
svn_skel_t *window_skel = chunk_skel->children->next;
svn_skel_t *diff_skel = window_skel->children;
+ apr_int64_t val;
+ apr_uint64_t uval;
+ const char *str;
/* Allocate a chunk and its window */
chunk = apr_palloc(pool, sizeof(*chunk));
/* Populate the window */
- chunk->version
- = (apr_byte_t)atoi(apr_pstrmemdup
- (pool,
- diff_skel->children->next->data,
- diff_skel->children->next->len));
+ str = apr_pstrmemdup(pool, diff_skel->children->next->data,
+ diff_skel->children->next->len);
+ SVN_ERR(svn_cstring_strtoui64(&uval, str, 0, 255, 10));
+ chunk->version = (apr_byte_t)uval;
+
chunk->string_key
= apr_pstrmemdup(pool,
diff_skel->children->next->next->data,
diff_skel->children->next->next->len);
- chunk->size
- = atoi(apr_pstrmemdup(pool,
- window_skel->children->next->data,
- window_skel->children->next->len));
+
+ str = apr_pstrmemdup(pool, window_skel->children->next->data,
+ window_skel->children->next->len);
+ SVN_ERR(svn_cstring_strtoui64(&uval, str, 0, APR_SIZE_MAX, 10));
+ chunk->size = (apr_size_t)uval;
+
chunk->rep_key
= apr_pstrmemdup(pool,
window_skel->children->next->next->data,
window_skel->children->next->next->len);
- chunk->offset =
- svn__atoui64(apr_pstrmemdup(pool,
- chunk_skel->children->data,
- chunk_skel->children->len));
+
+ str = apr_pstrmemdup(pool, chunk_skel->children->data,
+ chunk_skel->children->len);
+ SVN_ERR(svn_cstring_strtoi64(&val, str, 0, APR_INT64_MAX, 10));
+ chunk->offset = (svn_filesize_t)val;
/* Add this chunk to the array. */
APR_ARRAY_PUSH(chunks, rep_delta_chunk_t *) = chunk;
@@ -633,24 +640,28 @@ svn_fs_base__parse_node_revision_skel(no
noderev->predecessor_count = -1;
if (cur_skel->next)
{
+ const char *str;
+
cur_skel = cur_skel->next;
if (cur_skel->len)
- noderev->predecessor_count = atoi(apr_pstrmemdup(pool,
- cur_skel->data,
- cur_skel->len));
+ {
+ str = apr_pstrmemdup(pool, cur_skel->data, cur_skel->len);
+ SVN_ERR(svn_cstring_atoi(&noderev->predecessor_count, str));
+ }
/* HAS-MERGEINFO and MERGEINFO-COUNT */
if (cur_skel->next)
{
+ int val;
+
cur_skel = cur_skel->next;
- noderev->has_mergeinfo = atoi(apr_pstrmemdup(pool,
- cur_skel->data,
- cur_skel->len))
- != 0;
- noderev->mergeinfo_count =
- apr_atoi64(apr_pstrmemdup(pool,
- cur_skel->next->data,
- cur_skel->next->len));
+ str = apr_pstrmemdup(pool, cur_skel->data, cur_skel->len);
+ SVN_ERR(svn_cstring_atoi(&val, str));
+ noderev->has_mergeinfo = (val != 0);
+
+ str = apr_pstrmemdup(pool, cur_skel->next->data,
+ cur_skel->next->len);
+ SVN_ERR(svn_cstring_atoi64(&noderev->mergeinfo_count, str));
}
}
}
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs.h
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs.h?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs.h (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_fs_fs/fs.h Wed Sep 15 19:32:26 2010
@@ -125,6 +125,9 @@ extern "C" {
/* The minimum format number that supports packed revprop shards. */
#define SVN_FS_FS__MIN_PACKED_REVPROP_FORMAT 5
+/* The minimum format number that supports a configuration file (fsfs.conf) */
+#define SVN_FS_FS__MIN_CONFIG_FILE 4
+
/* Private FSFS-specific data shared between all svn_txn_t objects that
relate to a particular transaction in a filesystem (as identified
by transaction id and filesystem UUID). Objects of this type are