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 52svn_error_t * 53svn_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 140svn_error_t * 141svn_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 227void 228svn_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 242svn_error_t * 243svn_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 290svn_error_t * 291svn_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 351svn_error_t * 352svn_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 444svn_error_t * 445svn_wc__translated_stream(svn_stream_t **stream, 446 svn_wc_context_t *wc_ctx, 447 const char *local_abspath, 448 const char *versioned_abspath, 449 apr_uint32_t flags, 450 apr_pool_t *result_pool, 451 apr_pool_t *scratch_pool) 452{ 453 return svn_error_trace( 454 svn_wc__internal_translated_stream(stream, wc_ctx->db, 455 local_abspath, 456 versioned_abspath, 457 flags, result_pool, 458 scratch_pool)); 459} 460