You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by rh...@apache.org on 2013/01/20 20:15:45 UTC
svn commit: r1435916 - /subversion/trunk/subversion/libsvn_client/patch.c
Author: rhuijben
Date: Sun Jan 20 19:15:45 2013
New Revision: 1435916
URL: http://svn.apache.org/viewvc?rev=1435916&view=rev
Log:
Add some code in an attempt to fix issue #4273 "'svn patch' should change
symlink targets", from an operating system that doesn't support symlinks.
It doesn't break any tests on Windows and with a lot of luck might create an
XPass on posix.
* subversion/libsvn_client/patch.c
(patch_target_t): Add boolean to note that we are editting a symlink.
(resolve_target_path): Detect symlinks.
(symlink_baton_t): New struct.
(readline_symlink,
tell_symlink,
seek_symlink,
write_symlink): New functions. Handling symlinks as a one line file,
like our subst library does.
(init_patch_target): Hook up link handling, when needed.
Modified:
subversion/trunk/subversion/libsvn_client/patch.c
Modified: subversion/trunk/subversion/libsvn_client/patch.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/patch.c?rev=1435916&r1=1435915&r2=1435916&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/patch.c (original)
+++ subversion/trunk/subversion/libsvn_client/patch.c Sun Jan 20 19:15:45 2013
@@ -175,6 +175,9 @@ typedef struct patch_target_t {
* CONTENT->existed). */
apr_file_t *file;
+ /* The target file is a symlink */
+ svn_boolean_t is_symlink;
+
/* The patched file.
* This is equivalent to the target, except that in appropriate
* places it contains the modified text as it appears in the patch file.
@@ -451,8 +454,9 @@ resolve_target_path(patch_target_t *targ
return SVN_NO_ERROR;
}
- SVN_ERR(svn_io_check_path(target->local_abspath,
- &target->kind_on_disk, scratch_pool));
+ SVN_ERR(svn_io_check_special_path(target->local_abspath,
+ &target->kind_on_disk, &target->is_symlink,
+ scratch_pool));
err = svn_wc__node_is_status_deleted(&target->locally_deleted,
wc_ctx, target->local_abspath,
scratch_pool);
@@ -498,8 +502,10 @@ resolve_target_path(patch_target_t *targ
/* As far as we are concerned this target is not locally deleted. */
target->locally_deleted = FALSE;
- SVN_ERR(svn_io_check_path(target->local_abspath,
- &target->kind_on_disk, scratch_pool));
+ SVN_ERR(svn_io_check_special_path(target->local_abspath,
+ &target->kind_on_disk,
+ &target->is_symlink,
+ scratch_pool));
}
else if (target->kind_on_disk != svn_node_none)
{
@@ -807,6 +813,107 @@ write_file(void *baton, const char *buf,
return SVN_NO_ERROR;
}
+/* Baton for the (readline|tell|seek|write)_symlink functions. */
+struct symlink_baton_t
+{
+ const char *local_abspath;
+ svn_boolean_t at_eof;
+};
+
+/* Allocate *STRINGBUF in RESULT_POOL, and read into it one line from
+ * the symlink accessed via BATON.
+ *
+ * Copied from file handling:
+ * Reading stops either after a line-terminator was found,
+ * or if EOF is reached in which case *EOF is set to TRUE.
+ * The line-terminator is not stored in *STRINGBUF.
+ *
+ * The line-terminator is detected automatically and stored in *EOL
+ * if EOL is not NULL. If EOF is reached and FILE 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 *
+readline_symlink(void *baton, svn_stringbuf_t **line, const char **eol_str,
+ svn_boolean_t *eof, apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+
+ if (eof)
+ *eof = FALSE;
+ if (eol_str)
+ *eol_str = NULL;
+
+ if (sb->at_eof)
+ {
+ *line = NULL;
+ if (eof)
+ *eof = TRUE;
+ }
+ else
+ {
+ svn_string_t *dest;
+ SVN_ERR(svn_io_read_link(&dest, sb->local_abspath, scratch_pool));
+
+ *line = svn_stringbuf_createf(result_pool, "link %s", dest->data);
+
+ sb->at_eof = TRUE;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Return in *OFFSET whether we are before or after the link. */
+static svn_error_t *
+tell_symlink(void *baton, apr_off_t *offset, apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+
+ *offset = sb->at_eof ? 1 : 0;
+ return SVN_NO_ERROR;
+}
+
+/* Seek to the specified by OFFSET in the unpatched file content accessed
+ * via BATON. Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+seek_symlink(void *baton, apr_off_t offset, apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+
+ sb->at_eof = (offset != 0);
+ return SVN_NO_ERROR;
+}
+
+/* Write LEN bytes from BUF into the patched file content accessed
+ * via BATON. Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+write_symlink(void *baton, const char *buf, apr_size_t len,
+ apr_pool_t *scratch_pool)
+{
+ struct symlink_baton_t *sb = baton;
+ const char *new_name;
+ const char *link = apr_pstrndup(scratch_pool, buf, len);
+
+ if (strncmp(link, "link ", 5) != 0)
+ return svn_error_create(SVN_ERR_IO_WRITE_ERROR, NULL,
+ _("Invalid link representation"));
+
+ link += 5; /* Skip "link " */
+
+ /* We assume the entire symlink is written at once, as the patch
+ format is line based */
+
+ SVN_ERR(svn_io_create_unique_link(&new_name, sb->local_abspath, link,
+ ".tmp", scratch_pool));
+
+ SVN_ERR(svn_io_file_rename(new_name, sb->local_abspath, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
/* Return a suitable filename for the target of PATCH.
* Examine the ``old'' and ``new'' file names, and choose the file name
* with the fewest path components, the shortest basename, and the shortest
@@ -912,7 +1019,20 @@ init_patch_target(patch_target_t **patch
/* Create a temporary file to write the patched result to.
* Also grab various bits of information about the file. */
- if (target->kind_on_disk == svn_node_file)
+ if (target->is_symlink)
+ {
+ struct symlink_baton_t *sb = apr_pcalloc(result_pool, sizeof(*sb));
+ content->existed = TRUE;
+
+ sb->local_abspath = target->local_abspath;
+
+ /* Wire up the read callbacks. */
+ content->readline = readline_symlink;
+ content->seek = seek_symlink;
+ content->tell = tell_symlink;
+ content->read_baton = target->file;
+ }
+ else if (target->kind_on_disk == svn_node_file)
{
SVN_ERR(svn_io_file_open(&target->file, target->local_abspath,
APR_READ | APR_BUFFERED,
@@ -950,17 +1070,26 @@ init_patch_target(patch_target_t **patch
else if (patch->operation == svn_diff_op_deleted)
target->deleted = TRUE;
- /* Open a temporary file to write the patched result to. */
- SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
- &target->patched_path, NULL,
- remove_tempfiles ?
- svn_io_file_del_on_pool_cleanup :
- svn_io_file_del_none,
- result_pool, scratch_pool));
-
- /* Put the write callback in place. */
- content->write = write_file;
- content->write_baton = target->patched_file;
+ if (! target->is_symlink)
+ {
+ /* Open a temporary file to write the patched result to. */
+ SVN_ERR(svn_io_open_unique_file3(&target->patched_file,
+ &target->patched_path, NULL,
+ remove_tempfiles ?
+ svn_io_file_del_on_pool_cleanup :
+ svn_io_file_del_none,
+ result_pool, scratch_pool));
+
+ /* Put the write callback in place. */
+ content->write = write_file;
+ content->write_baton = target->patched_file;
+ }
+ else
+ {
+ /* Put the write callback in place. */
+ content->write = write_symlink;
+ content->write_baton = content->read_baton;
+ }
/* Open a temporary file to write rejected hunks to. */
SVN_ERR(svn_io_open_unique_file3(&target->reject_file,