diff -ruN subversion/include/private/svn_subst_private.h subversion/include/private/svn_subst_private.h --- subversion/include/private/svn_subst_private.h 1970-01-01 03:00:00.000000000 +0300 +++ subversion/include/private/svn_subst_private.h 2012-04-02 18:49:12.000000000 +0400 @@ -0,0 +1,68 @@ +/** + * @copyright + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * @endcopyright + * + * @file svn_subst_private.h + * @brief Non-public subst utility functions. + */ + + +#ifndef SVN_SUBST_PRIVATE_H +#define SVN_SUBST_PRIVATE_H + +#include "svn_subst.h" /* for svn_boolean_t, svn_error_t */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** + * Set @a *kw to a new keywords hash filled with the appropriate contents + * given a @a keywords_string (the contents of the svn:keywords + * property for the file in question), the revision @a rev, the @a url, + * the url of the root of the @a repos, the @a date the file was committed + * on, and the @a author of the last commit. Any of these can be @c NULL + * to indicate that the information is not present, or @c 0 for @a date. + * + * Hash keys are of type const char *. + * Hash values are of type svn_string_t *. + * + * All memory is allocated out of @a pool. + * + * @since New in 1.6 + */ +svn_error_t * +svn_subst_build_keywords3(apr_hash_t **kw, + const char *keywords_string, + const char *rev, + const char *url, + const char *repos, + apr_time_t date, + const char *author, + apr_pool_t *pool); + + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_SUBST_PRIVATE_H */ diff -ruN subversion/libsvn_client/cat.c subversion/libsvn_client/cat.c --- subversion/libsvn_client/cat.c 2011-08-18 18:05:35.000000000 +0400 +++ subversion/libsvn_client/cat.c 2012-04-02 18:46:56.000000000 +0400 @@ -40,6 +40,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" +#include "private/svn_subst_private.h" /*** Code. ***/ @@ -137,12 +138,15 @@ const char *rev_str; const char *author; const char *url; + const char *repos; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author, wc_ctx, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, local_abspath, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__node_get_repos_info(&repos, NULL, wc_ctx, local_abspath, + scratch_pool, scratch_pool)); if (local_mod) { @@ -158,8 +162,8 @@ rev_str = apr_psprintf(scratch_pool, "%ld", changed_rev); } - SVN_ERR(svn_subst_build_keywords2(&kw, keywords->data, rev_str, url, tm, - author, scratch_pool)); + SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, rev_str, url, + repos, tm, author, scratch_pool)); } /* Wrap the output stream if translation is needed. */ @@ -188,6 +192,7 @@ svn_string_t *keywords; apr_hash_t *props; const char *url; + const char *repos_root_url; svn_stream_t *output = out; svn_error_t *err; @@ -231,6 +236,8 @@ &url, path_or_url, NULL, peg_revision, revision, ctx, pool)); + /* Find the repos root URL */ + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); /* Grab some properties we need to know in order to figure out if anything special needs to be done with this file. */ @@ -281,10 +288,11 @@ if (cmt_date) SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, pool)); - SVN_ERR(svn_subst_build_keywords2 + SVN_ERR(svn_subst_build_keywords3 (&kw, keywords->data, cmt_rev->data, url, + repos_root_url, when, cmt_author ? cmt_author->data : NULL, pool)); diff -ruN subversion/libsvn_client/commit.c subversion/libsvn_client/commit.c --- subversion/libsvn_client/commit.c 2011-11-28 21:04:12.000000000 +0400 +++ subversion/libsvn_client/commit.c 2012-04-02 18:46:52.000000000 +0400 @@ -50,6 +50,7 @@ #include "client.h" #include "private/svn_wc_private.h" #include "private/svn_magic.h" +#include "private/svn_subst_private.h" #include "svn_private_config.h" @@ -126,9 +127,9 @@ } if (keywords_val) - SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_val->data, + SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, APR_STRINGIFY(SVN_INVALID_REVNUM), - "", 0, "", pool)); + "", "", 0, "", pool)); else keywords = NULL; diff -ruN subversion/libsvn_client/export.c subversion/libsvn_client/export.c --- subversion/libsvn_client/export.c 2012-02-10 08:00:30.000000000 +0400 +++ subversion/libsvn_client/export.c 2012-04-02 18:46:47.000000000 +0400 @@ -43,6 +43,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" +#include "private/svn_subst_private.h" /*** Code. ***/ @@ -274,6 +275,7 @@ svn_revnum_t changed_rev; const char *suffix; const char *url; + const char *repos; const char *author; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author, @@ -296,11 +298,13 @@ SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, from_abspath, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__node_get_repos_info(&repos, NULL, wc_ctx, from_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(svn_subst_build_keywords2 + SVN_ERR(svn_subst_build_keywords3 (&kw, keywords->data, apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix), - url, tm, author, scratch_pool)); + url, repos, tm, author, scratch_pool)); } /* For atomicity, we translate to a tmp file and then rename the tmp file @@ -702,6 +706,7 @@ /* Any keyword vals to be substituted */ const char *revision; const char *url; + const char *repos; const char *author; apr_time_t date; @@ -823,6 +828,7 @@ fb->edit_baton = eb; fb->path = full_path; fb->url = full_url; + fb->repos = eb->root_url; fb->pool = pool; *baton = fb; @@ -987,8 +993,8 @@ } if (fb->keywords_val) - SVN_ERR(svn_subst_build_keywords2(&final_kw, fb->keywords_val->data, - fb->revision, fb->url, fb->date, + SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data, + fb->revision, fb->url, fb->repos, fb->date, fb->author, pool)); SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path, diff -ruN subversion/libsvn_subr/subst.c subversion/libsvn_subr/subst.c --- subversion/libsvn_subr/subst.c 2011-07-29 21:28:11.000000000 +0400 +++ subversion/libsvn_subr/subst.c 2012-04-02 18:47:06.000000000 +0400 @@ -49,6 +49,7 @@ #include "svn_private_config.h" #include "private/svn_string_private.h" +#include "private/svn_subst_private.h" /** * The textual elements of a detranslated special file. One of these @@ -135,8 +136,11 @@ * %b basename of the URL of this file * %d short format of date of this revision * %D long format of date of this revision + * %P path relative to root of repos * %r number of this revision + * %R root url of repository * %u URL of this file + * %_ a space * %% a literal % * * All memory is allocated out of @a pool. @@ -145,12 +149,14 @@ keyword_printf(const char *fmt, const char *rev, const char *url, + const char *repos, apr_time_t date, const char *author, apr_pool_t *pool) { svn_stringbuf_t *value = svn_stringbuf_ncreate("", 0, pool); const char *cur; + const char *relative; size_t n; for (;;) @@ -203,6 +209,23 @@ svn_stringbuf_appendcstr(value, svn_time_to_human_cstring(date, pool)); break; + case 'P': /* relative path of this file */ + relative = url; + if (relative && repos) + { + int len = strlen(repos); + + if (strncmp(repos, relative, len) == 0 + && relative[len] == '/') + relative += len + 1; + } + if (relative) + svn_stringbuf_appendcstr(value, relative); + break; + case 'R': /* root of repos */ + if (repos) + svn_stringbuf_appendcstr(value, repos); + break; case 'r': /* number of this revision */ if (rev) svn_stringbuf_appendcstr(value, rev); @@ -211,6 +234,9 @@ if (url) svn_stringbuf_appendcstr(value, url); break; + case '_': /* '%_' => a space */ + svn_stringbuf_appendbytes(value, " ", 1); + break; case '%': /* '%%' => a literal % */ svn_stringbuf_appendbyte(value, *cur); break; @@ -246,8 +272,8 @@ apr_hash_t *kwhash; const svn_string_t *val; - SVN_ERR(svn_subst_build_keywords2(&kwhash, keywords_val, rev, - url, date, author, pool)); + SVN_ERR(svn_subst_build_keywords3(&kwhash, keywords_val, rev, + url, "", date, author, pool)); /* The behaviour of pre-1.3 svn_subst_build_keywords, which we are * replicating here, is to write to a slot in the svn_subst_keywords_t @@ -286,6 +312,21 @@ const char *author, apr_pool_t *pool) { + SVN_ERR(svn_subst_build_keywords3(kw, keywords_val, rev, + url, "", date, author, pool)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_subst_build_keywords3(apr_hash_t **kw, + const char *keywords_val, + const char *rev, + const char *url, + const char *repos, + apr_time_t date, + const char *author, + apr_pool_t *pool) +{ apr_array_header_t *keyword_tokens; int i; *kw = apr_hash_make(pool); @@ -296,6 +337,24 @@ for (i = 0; i < keyword_tokens->nelts; ++i) { const char *keyword = APR_ARRAY_IDX(keyword_tokens, i, const char *); + apr_array_header_t *keyword_tokens2; + + keyword_tokens2 = svn_cstring_split(keyword, "=", TRUE /* chop */, pool); + if (keyword_tokens2->nelts == 2) + { + svn_string_t *custom_val; + const char *custom_expand; + + keyword = APR_ARRAY_IDX(keyword_tokens2, 0, const char*); + custom_expand = APR_ARRAY_IDX(keyword_tokens2, 1, const char*); + if (! strcmp(custom_expand, "%H")) + custom_expand = "%P %r %d %a"; + else if (! strcmp(custom_expand, "%I")) + custom_expand = "%b %r %d %a"; + custom_val = keyword_printf(custom_expand, rev, url, repos, date, author, pool); + apr_hash_set(*kw, keyword, APR_HASH_KEY_STRING, custom_val); + return SVN_NO_ERROR; + } if ((! strcmp(keyword, SVN_KEYWORD_REVISION_LONG)) || (! strcmp(keyword, SVN_KEYWORD_REVISION_MEDIUM)) @@ -303,7 +362,7 @@ { svn_string_t *revision_val; - revision_val = keyword_printf("%r", rev, url, date, author, pool); + revision_val = keyword_printf("%r", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_REVISION_LONG, APR_HASH_KEY_STRING, revision_val); apr_hash_set(*kw, SVN_KEYWORD_REVISION_MEDIUM, @@ -316,7 +375,7 @@ { svn_string_t *date_val; - date_val = keyword_printf("%D", rev, url, date, author, pool); + date_val = keyword_printf("%D", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_DATE_LONG, APR_HASH_KEY_STRING, date_val); apr_hash_set(*kw, SVN_KEYWORD_DATE_SHORT, @@ -327,7 +386,7 @@ { svn_string_t *author_val; - author_val = keyword_printf("%a", rev, url, date, author, pool); + author_val = keyword_printf("%a", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_AUTHOR_LONG, APR_HASH_KEY_STRING, author_val); apr_hash_set(*kw, SVN_KEYWORD_AUTHOR_SHORT, @@ -338,7 +397,7 @@ { svn_string_t *url_val; - url_val = keyword_printf("%u", rev, url, date, author, pool); + url_val = keyword_printf("%u", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_URL_LONG, APR_HASH_KEY_STRING, url_val); apr_hash_set(*kw, SVN_KEYWORD_URL_SHORT, @@ -348,7 +407,7 @@ { svn_string_t *id_val; - id_val = keyword_printf("%b %r %d %a", rev, url, date, author, + id_val = keyword_printf("%b %r %d %a", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_ID, APR_HASH_KEY_STRING, id_val); @@ -357,7 +416,7 @@ { svn_string_t *header_val; - header_val = keyword_printf("%u %r %d %a", rev, url, date, author, + header_val = keyword_printf("%u %r %d %a", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_HEADER, APR_HASH_KEY_STRING, header_val); diff -ruN subversion/libsvn_wc/translate.c subversion/libsvn_wc/translate.c --- subversion/libsvn_wc/translate.c 2011-07-07 15:29:08.000000000 +0400 +++ subversion/libsvn_wc/translate.c 2012-04-02 18:47:00.000000000 +0400 @@ -46,6 +46,7 @@ #include "svn_private_config.h" #include "private/svn_wc_private.h" +#include "private/svn_subst_private.h" @@ -313,10 +314,10 @@ apr_time_t changed_date; const char *changed_author; const char *url; + const char *repos_root_url; if (! for_normalization) { - const char *repos_root_url; const char *repos_relpath; SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, @@ -341,13 +342,23 @@ changed_rev = SVN_INVALID_REVNUM; changed_date = 0; changed_author = ""; + + SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, + &repos_root_url, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); } - SVN_ERR(svn_subst_build_keywords2(keywords, + SVN_ERR(svn_subst_build_keywords3(keywords, keyword_list, apr_psprintf(scratch_pool, "%ld", changed_rev), url, + repos_root_url, changed_date, changed_author, result_pool));