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