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 /* ==================================================================== */
26 #include "svn_client.h"
27 #include "svn_string.h"
28 #include "svn_error.h"
29 #include "svn_subst.h"
33 #include "svn_props.h"
36 #include "svn_private_config.h"
37 #include "private/svn_wc_private.h"
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. */
47 cat_local_file(const char *path
,
49 svn_wc_adm_access_t
*adm_access
,
50 const svn_opt_revision_t
*revision
,
53 const svn_wc_entry_t
*entry
;
54 apr_hash_t
*kw
= NULL
;
55 svn_subst_eol_style_t style
;
58 svn_string_t
*eol_style
, *keywords
, *special
;
59 const char *eol
= NULL
;
60 svn_boolean_t local_mod
= FALSE
;
62 apr_file_t
*input_file
;
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
));
84 svn_wc_status2_t
*status
;
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
)
93 eol_style
= apr_hash_get(props
, SVN_PROP_EOL_STYLE
,
95 keywords
= apr_hash_get(props
, SVN_PROP_KEYWORDS
,
97 special
= apr_hash_get(props
, SVN_PROP_SPECIAL
,
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
107 SVN_ERR(svn_io_file_affected_time(&tm
, path
, pool
));
111 tm
= entry
->cmt_date
;
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 */
126 author
= _("(local)");
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
);
145 SVN_ERR(svn_subst_translate_stream3(input
, output
, eol
, FALSE
, kw
,
148 SVN_ERR(svn_stream_copy(input
, output
, pool
));
150 SVN_ERR(svn_stream_close(input
));
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
,
163 svn_ra_session_t
*ra_session
;
165 svn_node_kind_t url_kind
;
166 svn_string_t
*eol_style
;
167 svn_string_t
*keywords
;
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
,
189 SVN_ERR(cat_local_file(path_or_url
, out
, adm_access
, revision
, pool
));
191 SVN_ERR(svn_wc_adm_close(adm_access
));
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
,
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
;
223 svn_subst_eol_style_from_value(&eol
, &eol_str
, eol_style
->data
);
226 eol
= svn_subst_eol_style_none
;
233 svn_string_t
*cmt_rev
, *cmt_date
, *cmt_author
;
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
);
243 SVN_ERR(svn_time_from_cstring(&when
, cmt_date
->data
, pool
));
245 SVN_ERR(svn_subst_build_keywords2
246 (&kw
, keywords
->data
,
250 cmt_author
? cmt_author
->data
: 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
));
264 /* Close the interjected stream */
265 SVN_ERR(svn_stream_close(output
));
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
,
277 return svn_client_cat2(out
, path_or_url
, revision
, revision
,