Change the format of the revprops block sent in svnserve for
[svn.git] / subversion / libsvn_client / cat.c
blob7474c5ac23f805e2169f2d312e227afaac54fbcd
1 /*
2 * cat.c: implementation of the 'cat' command
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
23 /*** Includes. ***/
25 #include <assert.h>
26 #include "svn_client.h"
27 #include "svn_string.h"
28 #include "svn_error.h"
29 #include "svn_subst.h"
30 #include "svn_io.h"
31 #include "svn_time.h"
32 #include "svn_path.h"
33 #include "svn_props.h"
34 #include "client.h"
36 #include "svn_private_config.h"
37 #include "private/svn_wc_private.h"
40 /*** Code. ***/
42 /* Helper function to handle copying a potentially translated version of
43 local file PATH to OUTPUT. REVISION must be one of the following: BASE,
44 COMMITTED, WORKING, or UNSPECIFIED. If the revision is UNSPECIFIED, it
45 will default to BASE. Uses POOL for temporary allocations. */
46 static svn_error_t *
47 cat_local_file(const char *path,
48 svn_stream_t *output,
49 svn_wc_adm_access_t *adm_access,
50 const svn_opt_revision_t *revision,
51 apr_pool_t *pool)
53 const svn_wc_entry_t *entry;
54 apr_hash_t *kw = NULL;
55 svn_subst_eol_style_t style;
56 apr_hash_t *props;
57 const char *base;
58 svn_string_t *eol_style, *keywords, *special;
59 const char *eol = NULL;
60 svn_boolean_t local_mod = FALSE;
61 apr_time_t tm;
62 apr_file_t *input_file;
63 svn_stream_t *input;
65 assert(revision->kind == svn_opt_revision_working ||
66 revision->kind == svn_opt_revision_base ||
67 revision->kind == svn_opt_revision_committed ||
68 revision->kind == svn_opt_revision_unspecified);
70 SVN_ERR(svn_wc__entry_versioned(&entry, path, adm_access, FALSE, pool));
72 if (entry->kind != svn_node_file)
73 return svn_error_createf(SVN_ERR_CLIENT_IS_DIRECTORY, NULL,
74 _("'%s' refers to a directory"),
75 svn_path_local_style(path, pool));
77 if (revision->kind != svn_opt_revision_working)
79 SVN_ERR(svn_wc_get_pristine_copy_path(path, &base, pool));
80 SVN_ERR(svn_wc_get_prop_diffs(NULL, &props, path, adm_access, pool));
82 else
84 svn_wc_status2_t *status;
86 base = path;
87 SVN_ERR(svn_wc_prop_list(&props, path, adm_access, pool));
88 SVN_ERR(svn_wc_status2(&status, path, adm_access, pool));
89 if (status->text_status != svn_wc_status_normal)
90 local_mod = TRUE;
93 eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE,
94 APR_HASH_KEY_STRING);
95 keywords = apr_hash_get(props, SVN_PROP_KEYWORDS,
96 APR_HASH_KEY_STRING);
97 special = apr_hash_get(props, SVN_PROP_SPECIAL,
98 APR_HASH_KEY_STRING);
100 if (eol_style)
101 svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
103 if (local_mod && (! special))
105 /* Use the modified time from the working copy if
106 the file */
107 SVN_ERR(svn_io_file_affected_time(&tm, path, pool));
109 else
111 tm = entry->cmt_date;
114 if (keywords)
116 const char *fmt;
117 const char *author;
119 if (local_mod)
121 /* For locally modified files, we'll append an 'M'
122 to the revision number, and set the author to
123 "(local)" since we can't always determine the
124 current user's username */
125 fmt = "%ldM";
126 author = _("(local)");
128 else
130 fmt = "%ld";
131 author = entry->cmt_author;
134 SVN_ERR(svn_subst_build_keywords2
135 (&kw, keywords->data,
136 apr_psprintf(pool, fmt, entry->cmt_rev),
137 entry->url, tm, author, pool));
140 SVN_ERR(svn_io_file_open(&input_file, base,
141 APR_READ, APR_OS_DEFAULT, pool));
142 input = svn_stream_from_aprfile2(input_file, FALSE, pool);
144 if ( eol || kw )
145 SVN_ERR(svn_subst_translate_stream3(input, output, eol, FALSE, kw,
146 TRUE, pool));
147 else
148 SVN_ERR(svn_stream_copy(input, output, pool));
150 SVN_ERR(svn_stream_close(input));
152 return SVN_NO_ERROR;
155 svn_error_t *
156 svn_client_cat2(svn_stream_t *out,
157 const char *path_or_url,
158 const svn_opt_revision_t *peg_revision,
159 const svn_opt_revision_t *revision,
160 svn_client_ctx_t *ctx,
161 apr_pool_t *pool)
163 svn_ra_session_t *ra_session;
164 svn_revnum_t rev;
165 svn_node_kind_t url_kind;
166 svn_string_t *eol_style;
167 svn_string_t *keywords;
168 apr_hash_t *props;
169 const char *url;
170 svn_stream_t *output = out;
172 if (! svn_path_is_url(path_or_url)
173 && (peg_revision->kind == svn_opt_revision_base
174 || peg_revision->kind == svn_opt_revision_working
175 || peg_revision->kind == svn_opt_revision_committed
176 || peg_revision->kind == svn_opt_revision_unspecified)
177 && (revision->kind == svn_opt_revision_base
178 || revision->kind == svn_opt_revision_working
179 || revision->kind == svn_opt_revision_committed
180 || revision->kind == svn_opt_revision_unspecified))
182 svn_wc_adm_access_t *adm_access;
184 SVN_ERR(svn_wc_adm_open3(&adm_access, NULL,
185 svn_path_dirname(path_or_url, pool), FALSE,
186 0, ctx->cancel_func, ctx->cancel_baton,
187 pool));
189 SVN_ERR(cat_local_file(path_or_url, out, adm_access, revision, pool));
191 SVN_ERR(svn_wc_adm_close(adm_access));
193 return SVN_NO_ERROR;
196 /* Get an RA plugin for this filesystem object. */
197 SVN_ERR(svn_client__ra_session_from_path(&ra_session, &rev,
198 &url, path_or_url, NULL,
199 peg_revision,
200 revision, ctx, pool));
202 /* Make sure the object isn't a directory. */
203 SVN_ERR(svn_ra_check_path(ra_session, "", rev, &url_kind, pool));
204 if (url_kind == svn_node_dir)
205 return svn_error_createf(SVN_ERR_CLIENT_IS_DIRECTORY, NULL,
206 _("URL '%s' refers to a directory"), url);
208 /* Grab some properties we need to know in order to figure out if anything
209 special needs to be done with this file. */
210 SVN_ERR(svn_ra_get_file(ra_session, "", rev, NULL, NULL, &props, pool));
212 eol_style = apr_hash_get(props, SVN_PROP_EOL_STYLE, APR_HASH_KEY_STRING);
213 keywords = apr_hash_get(props, SVN_PROP_KEYWORDS, APR_HASH_KEY_STRING);
215 if (eol_style || keywords)
217 /* It's a file with no special eol style or keywords. */
218 svn_subst_eol_style_t eol;
219 const char *eol_str;
220 apr_hash_t *kw;
222 if (eol_style)
223 svn_subst_eol_style_from_value(&eol, &eol_str, eol_style->data);
224 else
226 eol = svn_subst_eol_style_none;
227 eol_str = NULL;
231 if (keywords)
233 svn_string_t *cmt_rev, *cmt_date, *cmt_author;
234 apr_time_t when = 0;
236 cmt_rev = apr_hash_get(props, SVN_PROP_ENTRY_COMMITTED_REV,
237 APR_HASH_KEY_STRING);
238 cmt_date = apr_hash_get(props, SVN_PROP_ENTRY_COMMITTED_DATE,
239 APR_HASH_KEY_STRING);
240 cmt_author = apr_hash_get(props, SVN_PROP_ENTRY_LAST_AUTHOR,
241 APR_HASH_KEY_STRING);
242 if (cmt_date)
243 SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, pool));
245 SVN_ERR(svn_subst_build_keywords2
246 (&kw, keywords->data,
247 cmt_rev->data,
248 url,
249 when,
250 cmt_author ? cmt_author->data : NULL,
251 pool));
253 else
254 kw = NULL;
256 /* Interject a translating stream */
257 output = svn_subst_stream_translated(svn_stream_disown(out, pool),
258 eol_str, FALSE, kw, TRUE, pool);
261 SVN_ERR(svn_ra_get_file(ra_session, "", rev, output, NULL, NULL, pool));
263 if (out != output)
264 /* Close the interjected stream */
265 SVN_ERR(svn_stream_close(output));
267 return SVN_NO_ERROR;
270 svn_error_t *
271 svn_client_cat(svn_stream_t *out,
272 const char *path_or_url,
273 const svn_opt_revision_t *revision,
274 svn_client_ctx_t *ctx,
275 apr_pool_t *pool)
277 return svn_client_cat2(out, path_or_url, revision, revision,
278 ctx, pool);