1 /*
2 * translate.c : wc-specific eol/keyword substitution
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <apr_pools.h>
30 #include <apr_file_io.h>
31 #include <apr_strings.h>
32
33 #include "svn_private_config.h"
34 #include "svn_types.h"
35 #include "svn_string.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_hash.h"
38 #include "svn_path.h"
39 #include "svn_error.h"
40 #include "svn_subst.h"
41 #include "svn_io.h"
42 #include "svn_props.h"
43
44 #include "wc.h"
45 #include "adm_files.h"
46 #include "translate.h"
47 #include "props.h"
48
49 #include "private/svn_wc_private.h"
50
51
52 svn_error_t *
svn_wc__internal_translated_stream(svn_stream_t ** stream,svn_wc__db_t * db,const char * local_abspath,const char * versioned_abspath,apr_uint32_t flags,apr_pool_t * result_pool,apr_pool_t * scratch_pool)53 svn_wc__internal_translated_stream(svn_stream_t **stream,
54 svn_wc__db_t *db,
55 const char *local_abspath,
56 const char *versioned_abspath,
57 apr_uint32_t flags,
58 apr_pool_t *result_pool,
59 apr_pool_t *scratch_pool)
60 {
61 svn_boolean_t special;
62 svn_boolean_t to_nf = flags & SVN_WC_TRANSLATE_TO_NF;
63 svn_subst_eol_style_t style;
64 const char *eol;
65 apr_hash_t *keywords;
66 svn_boolean_t repair_forced = flags & SVN_WC_TRANSLATE_FORCE_EOL_REPAIR;
67
68 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
69 SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_abspath));
70
71 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
72 &keywords,
73 &special,
74 db, versioned_abspath, NULL, FALSE,
75 scratch_pool, scratch_pool));
76
77 if (special)
78 {
79 if (to_nf)
80 return svn_subst_read_specialfile(stream, local_abspath, result_pool,
81 scratch_pool);
82
83 return svn_subst_create_specialfile(stream, local_abspath, result_pool,
84 scratch_pool);
85 }
86
87 if (to_nf)
88 SVN_ERR(svn_stream_open_readonly(stream, local_abspath, result_pool,
89 scratch_pool));
90 else
91 {
92 apr_file_t *file;
93
94 /* We don't want the "open-exclusively" feature of the normal
95 svn_stream_open_writable interface. Do this manually. */
96 SVN_ERR(svn_io_file_open(&file, local_abspath,
97 APR_CREATE | APR_WRITE | APR_BUFFERED,
98 APR_OS_DEFAULT, result_pool));
99 *stream = svn_stream_from_aprfile2(file, FALSE, result_pool);
100 }
101
102 if (svn_subst_translation_required(style, eol, keywords, special, TRUE))
103 {
104 if (to_nf)
105 {
106 if (style == svn_subst_eol_style_native)
107 eol = SVN_SUBST_NATIVE_EOL_STR;
108 else if (style == svn_subst_eol_style_fixed)
109 repair_forced = TRUE;
110 else if (style != svn_subst_eol_style_none)
111 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
112
113 /* Wrap the stream to translate to normal form */
114 *stream = svn_subst_stream_translated(*stream,
115 eol,
116 repair_forced,
117 keywords,
118 FALSE /* expand */,
119 result_pool);
120
121 /* streams enforce our contract that TO_NF streams are read-only
122 * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to
123 * write to them. */
124 }
125 else
126 {
127 *stream = svn_subst_stream_translated(*stream, eol, TRUE,
128 keywords, TRUE, result_pool);
129
130 /* streams enforce our contract that FROM_NF streams are write-only
131 * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to
132 * read them. */
133 }
134 }
135
136 return SVN_NO_ERROR;
137 }
138
139
140 svn_error_t *
svn_wc__internal_translated_file(const char ** xlated_abspath,const char * src_abspath,svn_wc__db_t * db,const char * versioned_abspath,apr_uint32_t flags,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)141 svn_wc__internal_translated_file(const char **xlated_abspath,
142 const char *src_abspath,
143 svn_wc__db_t *db,
144 const char *versioned_abspath,
145 apr_uint32_t flags,
146 svn_cancel_func_t cancel_func,
147 void *cancel_baton,
148 apr_pool_t *result_pool,
149 apr_pool_t *scratch_pool)
150 {
151 svn_subst_eol_style_t style;
152 const char *eol;
153 apr_hash_t *keywords;
154 svn_boolean_t special;
155
156 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
157 SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_abspath));
158 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
159 &keywords,
160 &special,
161 db, versioned_abspath, NULL, FALSE,
162 scratch_pool, scratch_pool));
163
164 if (! svn_subst_translation_required(style, eol, keywords, special, TRUE)
165 && (! (flags & SVN_WC_TRANSLATE_FORCE_COPY)))
166 {
167 /* Translation would be a no-op, so return the original file. */
168 *xlated_abspath = src_abspath;
169 }
170 else /* some translation (or copying) is necessary */
171 {
172 const char *tmp_dir;
173 const char *tmp_vfile;
174 svn_boolean_t repair_forced
175 = (flags & SVN_WC_TRANSLATE_FORCE_EOL_REPAIR) != 0;
176 svn_boolean_t expand = (flags & SVN_WC_TRANSLATE_TO_NF) == 0;
177
178 if (flags & SVN_WC_TRANSLATE_USE_GLOBAL_TMP)
179 tmp_dir = NULL;
180 else
181 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir, db, versioned_abspath,
182 scratch_pool, scratch_pool));
183
184 SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_vfile, tmp_dir,
185 (flags & SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP)
186 ? svn_io_file_del_none
187 : svn_io_file_del_on_pool_cleanup,
188 result_pool, scratch_pool));
189
190 /* ### ugh. the repair behavior does NOT match the docstring. bleah.
191 ### all of these translation functions are crap and should go
192 ### away anyways. we'll just deprecate most of the functions and
193 ### properly document the survivors */
194
195 if (expand)
196 {
197 /* from normal form */
198
199 repair_forced = TRUE;
200 }
201 else
202 {
203 /* to normal form */
204
205 if (style == svn_subst_eol_style_native)
206 eol = SVN_SUBST_NATIVE_EOL_STR;
207 else if (style == svn_subst_eol_style_fixed)
208 repair_forced = TRUE;
209 else if (style != svn_subst_eol_style_none)
210 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
211 }
212
213 SVN_ERR(svn_subst_copy_and_translate4(src_abspath, tmp_vfile,
214 eol, repair_forced,
215 keywords,
216 expand,
217 special,
218 cancel_func, cancel_baton,
219 result_pool));
220
221 *xlated_abspath = tmp_vfile;
222 }
223
224 return SVN_NO_ERROR;
225 }
226
227 void
svn_wc__eol_value_from_string(const char ** value,const char * eol)228 svn_wc__eol_value_from_string(const char **value, const char *eol)
229 {
230 if (eol == NULL)
231 *value = NULL;
232 else if (! strcmp("\n", eol))
233 *value = "LF";
234 else if (! strcmp("\r", eol))
235 *value = "CR";
236 else if (! strcmp("\r\n", eol))
237 *value = "CRLF";
238 else
239 *value = NULL;
240 }
241
242 svn_error_t *
svn_wc__get_translate_info(svn_subst_eol_style_t * style,const char ** eol,apr_hash_t ** keywords,svn_boolean_t * special,svn_wc__db_t * db,const char * local_abspath,apr_hash_t * props,svn_boolean_t for_normalization,apr_pool_t * result_pool,apr_pool_t * scratch_pool)243 svn_wc__get_translate_info(svn_subst_eol_style_t *style,
244 const char **eol,
245 apr_hash_t **keywords,
246 svn_boolean_t *special,
247 svn_wc__db_t *db,
248 const char *local_abspath,
249 apr_hash_t *props,
250 svn_boolean_t for_normalization,
251 apr_pool_t *result_pool,
252 apr_pool_t *scratch_pool)
253 {
254 const char *propval;
255 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
256
257 if (props == NULL)
258 SVN_ERR(svn_wc__get_actual_props(&props, db, local_abspath,
259 scratch_pool, scratch_pool));
260
261 if (eol)
262 {
263 propval = svn_prop_get_value(props, SVN_PROP_EOL_STYLE);
264
265 svn_subst_eol_style_from_value(style, eol, propval);
266 }
267
268 if (keywords)
269 {
270 propval = svn_prop_get_value(props, SVN_PROP_KEYWORDS);
271
272 if (!propval || *propval == '\0')
273 *keywords = NULL;
274 else
275 SVN_ERR(svn_wc__expand_keywords(keywords,
276 db, local_abspath, NULL,
277 propval, for_normalization,
278 result_pool, scratch_pool));
279 }
280 if (special)
281 {
282 propval = svn_prop_get_value(props, SVN_PROP_SPECIAL);
283
284 *special = (propval != NULL);
285 }
286
287 return SVN_NO_ERROR;
288 }
289
290 svn_error_t *
svn_wc__expand_keywords(apr_hash_t ** keywords,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * keyword_list,svn_boolean_t for_normalization,apr_pool_t * result_pool,apr_pool_t * scratch_pool)291 svn_wc__expand_keywords(apr_hash_t **keywords,
292 svn_wc__db_t *db,
293 const char *local_abspath,
294 const char *wri_abspath,
295 const char *keyword_list,
296 svn_boolean_t for_normalization,
297 apr_pool_t *result_pool,
298 apr_pool_t *scratch_pool)
299 {
300 svn_revnum_t changed_rev;
301 apr_time_t changed_date;
302 const char *changed_author;
303 const char *url;
304 const char *repos_root_url;
305
306 if (! for_normalization)
307 {
308 const char *repos_relpath;
309
310 SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath,
311 &repos_root_url, NULL, &changed_rev,
312 &changed_date, &changed_author, NULL,
313 NULL, NULL, NULL, NULL, NULL, NULL,
314 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
315 NULL, NULL, NULL, NULL,
316 db, local_abspath,
317 scratch_pool, scratch_pool));
318
319 /* Handle special statuses (e.g. added) */
320 if (!repos_relpath)
321 SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath,
322 &repos_root_url, NULL,
323 db, local_abspath,
324 scratch_pool, scratch_pool));
325
326 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
327 scratch_pool);
328 }
329 else
330 {
331 url = "";
332 changed_rev = SVN_INVALID_REVNUM;
333 changed_date = 0;
334 changed_author = "";
335 repos_root_url = "";
336 }
337
338 SVN_ERR(svn_subst_build_keywords3(keywords, keyword_list,
339 apr_psprintf(scratch_pool, "%ld",
340 changed_rev),
341 url, repos_root_url,
342 changed_date, changed_author,
343 result_pool));
344
345 if (apr_hash_count(*keywords) == 0)
346 *keywords = NULL;
347
348 return SVN_NO_ERROR;
349 }
350
351 svn_error_t *
svn_wc__sync_flags_with_props(svn_boolean_t * did_set,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)352 svn_wc__sync_flags_with_props(svn_boolean_t *did_set,
353 svn_wc__db_t *db,
354 const char *local_abspath,
355 apr_pool_t *scratch_pool)
356 {
357 svn_wc__db_status_t status;
358 svn_node_kind_t kind;
359 svn_wc__db_lock_t *lock;
360 apr_hash_t *props = NULL;
361 svn_boolean_t had_props;
362 svn_boolean_t props_mod;
363
364 if (did_set)
365 *did_set = FALSE;
366
367 /* ### We'll consolidate these info gathering statements in a future
368 commit. */
369
370 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
371 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
372 NULL, &lock, NULL, NULL, NULL, NULL, NULL,
373 &had_props, &props_mod, NULL, NULL, NULL,
374 db, local_abspath,
375 scratch_pool, scratch_pool));
376
377 /* We actually only care about the following flags on files, so just
378 early-out for all other types.
379
380 Also bail if there is no in-wc representation of the file. */
381 if (kind != svn_node_file
382 || (status != svn_wc__db_status_normal
383 && status != svn_wc__db_status_added))
384 return SVN_NO_ERROR;
385
386 if (props_mod || had_props)
387 SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool,
388 scratch_pool));
389 else
390 props = NULL;
391
392 /* If we get this far, we're going to change *something*, so just set
393 the flag appropriately. */
394 if (did_set)
395 *did_set = TRUE;
396
397 /* Handle the read-write bit. */
398 if (status != svn_wc__db_status_normal
399 || props == NULL
400 || ! svn_hash_gets(props, SVN_PROP_NEEDS_LOCK)
401 || lock)
402 {
403 SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
404 }
405 else
406 {
407 /* Special case: If we have an uncommitted svn:needs-lock, we don't
408 set the file read_only just yet. That happens upon commit. */
409 apr_hash_t *pristine_props;
410
411 if (! props_mod)
412 pristine_props = props;
413 else if (had_props)
414 SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, local_abspath,
415 scratch_pool, scratch_pool));
416 else
417 pristine_props = NULL;
418
419 if (pristine_props
420 && svn_hash_gets(pristine_props, SVN_PROP_NEEDS_LOCK) )
421 /*&& props
422 && apr_hash_get(props, SVN_PROP_NEEDS_LOCK, APR_HASH_KEY_STRING) )*/
423 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
424 }
425
426 /* Windows doesn't care about the execute bit. */
427 #ifndef WIN32
428
429 if (props == NULL
430 || ! svn_hash_gets(props, SVN_PROP_EXECUTABLE))
431 {
432 /* Turn off the execute bit */
433 SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE,
434 scratch_pool));
435 }
436 else
437 SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
438 scratch_pool));
439 #endif
440
441 return SVN_NO_ERROR;
442 }
443