# http://people.freebsd.org/~peter/svn_keyword.diff # # This patch is an extension of patch in issue #860. # Instead of aliasing one keyword to another, and a big bug (only # supports aliasing to $Id$ anyway), this version allows # keyword_printf() format strings. # svn propset svn:keywords 'Foo=%b Bar=%r' file (expands $Foo: filename $, etc) # %I is an alias for $id$-like expansion. I've added %_ to insert a space. # eg: 'CustomId=%b%_Some%_Random%_String'. The svn:keywords parser doesn't # allow plain spaces. # # it adds to keyword_printf(): # %P - $CVSHeader$ like repository-relative paths. # %_ - a space # %R - root of repository # and as aliases in the keyword builder: # %H - alias for $CVSHeader$ expansion. (%P %r %d %a) # %I - alias for $Id$ expansion. (%b %r %d %a) # the keyword builder aliases are because the svn:keyword splitter # uses spaces. # # I had to hack the API to support this. If there is a better way # to do it, please let me know. I'm sure I've done everything wrong. # # freebsd.org uses this patch, relative to 1.5, and sets # svn:keywords to "FreeBSD=%H'. This expands thus: # $FreeBSD: stable/7/bin/ls/ls.c 157160 2006-03-24 17:09:03Z jhb $ # $FreeBSD: head/bin/ls/ls.c 177989 2008-04-04 03:57:46Z grog $ # This works the same way as $CVSHeader$ in gnu-cvs. # Index: subversion/include/svn_subst.h =================================================================== --- subversion/include/svn_subst.h (revision 31461) +++ subversion/include/svn_subst.h (working copy) @@ -115,18 +115,33 @@ * 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 @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. + * 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.3. + * @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); + +/** Similar to svn_subst_build_keywords3() except that it does not + * supply the repository location. + * + * @deprecated Provided for backward compatibility with the 1.3 API. + */ +svn_error_t * svn_subst_build_keywords2(apr_hash_t **kw, const char *keywords_string, const char *rev, Index: subversion/libsvn_wc/translate.c =================================================================== --- subversion/libsvn_wc/translate.c (revision 31461) +++ subversion/libsvn_wc/translate.c (working copy) @@ -266,11 +266,12 @@ SVN_ERR(svn_wc__entry_versioned(&entry, path, adm_access, FALSE, pool)); - SVN_ERR(svn_subst_build_keywords2(keywords, + SVN_ERR(svn_subst_build_keywords3(keywords, list, apr_psprintf(pool, "%ld", entry->cmt_rev), entry->url, + entry->repos, entry->cmt_date, entry->cmt_author, pool)); Index: subversion/libsvn_subr/subst.c =================================================================== --- subversion/libsvn_subr/subst.c (revision 31461) +++ subversion/libsvn_subr/subst.c (working copy) @@ -180,8 +180,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. @@ -190,12 +193,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; int n; for (;;) @@ -249,6 +254,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); @@ -257,6 +279,9 @@ if (url) svn_stringbuf_appendcstr(value, url); break; + case '_': /* '%_' => a space */ + svn_stringbuf_appendbytes(value, " ", 1); + break; case '%': /* '%%' => a literal % */ svn_stringbuf_appendbytes(value, cur, 1); break; @@ -346,8 +371,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 @@ -386,6 +411,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); @@ -396,14 +436,32 @@ 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)) || (! svn_cstring_casecmp(keyword, SVN_KEYWORD_REVISION_SHORT))) { 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, @@ -416,7 +474,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, @@ -427,7 +485,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, @@ -438,7 +496,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, @@ -448,7 +506,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); Index: subversion/libsvn_client/export.c =================================================================== --- subversion/libsvn_client/export.c (revision 31461) +++ subversion/libsvn_client/export.c (working copy) @@ -188,10 +188,10 @@ author = entry->cmt_author; } - SVN_ERR(svn_subst_build_keywords2 + SVN_ERR(svn_subst_build_keywords3 (&kw, keywords->data, apr_psprintf(pool, fmt, entry->cmt_rev), - entry->url, tm, author, pool)); + entry->url, entry->repos, tm, author, pool)); } SVN_ERR(svn_subst_copy_and_translate3(base, to, eol, FALSE, @@ -474,6 +474,7 @@ /* Any keyword vals to be substituted */ const char *revision; const char *url; + const char *repos; const char *author; apr_time_t date; @@ -590,6 +591,7 @@ fb->edit_baton = eb; fb->path = full_path; fb->url = full_url; + fb->repos = eb->root_url; fb->pool = pool; *baton = fb; @@ -743,8 +745,8 @@ eb->native_eol)); 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_translate3 Index: subversion/libsvn_client/cat.c =================================================================== --- subversion/libsvn_client/cat.c (revision 31461) +++ subversion/libsvn_client/cat.c (working copy) @@ -131,10 +131,10 @@ author = entry->cmt_author; } - SVN_ERR(svn_subst_build_keywords2 + SVN_ERR(svn_subst_build_keywords3 (&kw, keywords->data, apr_psprintf(pool, fmt, entry->cmt_rev), - entry->url, tm, author, pool)); + entry->url, entry->repos, tm, author, pool)); } SVN_ERR(svn_io_file_open(&input_file, base, @@ -167,6 +167,7 @@ svn_string_t *keywords; apr_hash_t *props; const char *url; + const char *repos_root_url; svn_stream_t *output = out; if (! svn_path_is_url(path_or_url) @@ -198,6 +199,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)); /* Make sure the object isn't a directory. */ SVN_ERR(svn_ra_check_path(ra_session, "", rev, &url_kind, pool)); @@ -242,10 +245,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)); Index: subversion/libsvn_client/commit.c =================================================================== --- subversion/libsvn_client/commit.c (revision 31461) +++ subversion/libsvn_client/commit.c (working copy) @@ -118,9 +118,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;