1251881Speter/* 2251881Speter * translate.c : wc-specific eol/keyword substitution 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <stdlib.h> 27251881Speter#include <string.h> 28251881Speter 29251881Speter#include <apr_pools.h> 30251881Speter#include <apr_file_io.h> 31251881Speter#include <apr_strings.h> 32251881Speter 33299742Sdim#include "svn_private_config.h" 34251881Speter#include "svn_types.h" 35251881Speter#include "svn_string.h" 36251881Speter#include "svn_dirent_uri.h" 37251881Speter#include "svn_hash.h" 38251881Speter#include "svn_path.h" 39251881Speter#include "svn_error.h" 40251881Speter#include "svn_subst.h" 41251881Speter#include "svn_io.h" 42251881Speter#include "svn_props.h" 43251881Speter 44251881Speter#include "wc.h" 45251881Speter#include "adm_files.h" 46251881Speter#include "translate.h" 47251881Speter#include "props.h" 48251881Speter 49251881Speter#include "private/svn_wc_private.h" 50251881Speter 51251881Speter 52251881Spetersvn_error_t * 53251881Spetersvn_wc__internal_translated_stream(svn_stream_t **stream, 54251881Speter svn_wc__db_t *db, 55251881Speter const char *local_abspath, 56251881Speter const char *versioned_abspath, 57251881Speter apr_uint32_t flags, 58251881Speter apr_pool_t *result_pool, 59251881Speter apr_pool_t *scratch_pool) 60251881Speter{ 61251881Speter svn_boolean_t special; 62251881Speter svn_boolean_t to_nf = flags & SVN_WC_TRANSLATE_TO_NF; 63251881Speter svn_subst_eol_style_t style; 64251881Speter const char *eol; 65251881Speter apr_hash_t *keywords; 66251881Speter svn_boolean_t repair_forced = flags & SVN_WC_TRANSLATE_FORCE_EOL_REPAIR; 67251881Speter 68251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 69251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_abspath)); 70251881Speter 71251881Speter SVN_ERR(svn_wc__get_translate_info(&style, &eol, 72251881Speter &keywords, 73251881Speter &special, 74251881Speter db, versioned_abspath, NULL, FALSE, 75251881Speter scratch_pool, scratch_pool)); 76251881Speter 77251881Speter if (special) 78251881Speter { 79251881Speter if (to_nf) 80251881Speter return svn_subst_read_specialfile(stream, local_abspath, result_pool, 81251881Speter scratch_pool); 82251881Speter 83251881Speter return svn_subst_create_specialfile(stream, local_abspath, result_pool, 84251881Speter scratch_pool); 85251881Speter } 86251881Speter 87251881Speter if (to_nf) 88251881Speter SVN_ERR(svn_stream_open_readonly(stream, local_abspath, result_pool, 89251881Speter scratch_pool)); 90251881Speter else 91251881Speter { 92251881Speter apr_file_t *file; 93251881Speter 94251881Speter /* We don't want the "open-exclusively" feature of the normal 95251881Speter svn_stream_open_writable interface. Do this manually. */ 96251881Speter SVN_ERR(svn_io_file_open(&file, local_abspath, 97251881Speter APR_CREATE | APR_WRITE | APR_BUFFERED, 98251881Speter APR_OS_DEFAULT, result_pool)); 99251881Speter *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); 100251881Speter } 101251881Speter 102251881Speter if (svn_subst_translation_required(style, eol, keywords, special, TRUE)) 103251881Speter { 104251881Speter if (to_nf) 105251881Speter { 106251881Speter if (style == svn_subst_eol_style_native) 107251881Speter eol = SVN_SUBST_NATIVE_EOL_STR; 108251881Speter else if (style == svn_subst_eol_style_fixed) 109251881Speter repair_forced = TRUE; 110251881Speter else if (style != svn_subst_eol_style_none) 111251881Speter return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); 112251881Speter 113251881Speter /* Wrap the stream to translate to normal form */ 114251881Speter *stream = svn_subst_stream_translated(*stream, 115251881Speter eol, 116251881Speter repair_forced, 117251881Speter keywords, 118251881Speter FALSE /* expand */, 119251881Speter result_pool); 120251881Speter 121299742Sdim /* streams enforce our contract that TO_NF streams are read-only 122299742Sdim * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to 123299742Sdim * write to them. */ 124251881Speter } 125251881Speter else 126251881Speter { 127251881Speter *stream = svn_subst_stream_translated(*stream, eol, TRUE, 128251881Speter keywords, TRUE, result_pool); 129251881Speter 130299742Sdim /* streams enforce our contract that FROM_NF streams are write-only 131299742Sdim * by returning SVN_ERR_STREAM_NOT_SUPPORTED when trying to 132299742Sdim * read them. */ 133251881Speter } 134251881Speter } 135251881Speter 136251881Speter return SVN_NO_ERROR; 137251881Speter} 138251881Speter 139251881Speter 140251881Spetersvn_error_t * 141251881Spetersvn_wc__internal_translated_file(const char **xlated_abspath, 142251881Speter const char *src_abspath, 143251881Speter svn_wc__db_t *db, 144251881Speter const char *versioned_abspath, 145251881Speter apr_uint32_t flags, 146251881Speter svn_cancel_func_t cancel_func, 147251881Speter void *cancel_baton, 148251881Speter apr_pool_t *result_pool, 149251881Speter apr_pool_t *scratch_pool) 150251881Speter{ 151251881Speter svn_subst_eol_style_t style; 152251881Speter const char *eol; 153251881Speter apr_hash_t *keywords; 154251881Speter svn_boolean_t special; 155251881Speter 156251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); 157251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_abspath)); 158251881Speter SVN_ERR(svn_wc__get_translate_info(&style, &eol, 159251881Speter &keywords, 160251881Speter &special, 161251881Speter db, versioned_abspath, NULL, FALSE, 162251881Speter scratch_pool, scratch_pool)); 163251881Speter 164251881Speter if (! svn_subst_translation_required(style, eol, keywords, special, TRUE) 165251881Speter && (! (flags & SVN_WC_TRANSLATE_FORCE_COPY))) 166251881Speter { 167251881Speter /* Translation would be a no-op, so return the original file. */ 168251881Speter *xlated_abspath = src_abspath; 169251881Speter } 170251881Speter else /* some translation (or copying) is necessary */ 171251881Speter { 172251881Speter const char *tmp_dir; 173251881Speter const char *tmp_vfile; 174251881Speter svn_boolean_t repair_forced 175251881Speter = (flags & SVN_WC_TRANSLATE_FORCE_EOL_REPAIR) != 0; 176251881Speter svn_boolean_t expand = (flags & SVN_WC_TRANSLATE_TO_NF) == 0; 177251881Speter 178251881Speter if (flags & SVN_WC_TRANSLATE_USE_GLOBAL_TMP) 179251881Speter tmp_dir = NULL; 180251881Speter else 181251881Speter SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir, db, versioned_abspath, 182251881Speter scratch_pool, scratch_pool)); 183251881Speter 184251881Speter SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_vfile, tmp_dir, 185251881Speter (flags & SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP) 186251881Speter ? svn_io_file_del_none 187251881Speter : svn_io_file_del_on_pool_cleanup, 188251881Speter result_pool, scratch_pool)); 189251881Speter 190251881Speter /* ### ugh. the repair behavior does NOT match the docstring. bleah. 191251881Speter ### all of these translation functions are crap and should go 192251881Speter ### away anyways. we'll just deprecate most of the functions and 193251881Speter ### properly document the survivors */ 194251881Speter 195251881Speter if (expand) 196251881Speter { 197251881Speter /* from normal form */ 198251881Speter 199251881Speter repair_forced = TRUE; 200251881Speter } 201251881Speter else 202251881Speter { 203251881Speter /* to normal form */ 204251881Speter 205251881Speter if (style == svn_subst_eol_style_native) 206251881Speter eol = SVN_SUBST_NATIVE_EOL_STR; 207251881Speter else if (style == svn_subst_eol_style_fixed) 208251881Speter repair_forced = TRUE; 209251881Speter else if (style != svn_subst_eol_style_none) 210251881Speter return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); 211251881Speter } 212251881Speter 213251881Speter SVN_ERR(svn_subst_copy_and_translate4(src_abspath, tmp_vfile, 214251881Speter eol, repair_forced, 215251881Speter keywords, 216251881Speter expand, 217251881Speter special, 218251881Speter cancel_func, cancel_baton, 219251881Speter result_pool)); 220251881Speter 221251881Speter *xlated_abspath = tmp_vfile; 222251881Speter } 223251881Speter 224251881Speter return SVN_NO_ERROR; 225251881Speter} 226251881Speter 227251881Spetervoid 228251881Spetersvn_wc__eol_value_from_string(const char **value, const char *eol) 229251881Speter{ 230251881Speter if (eol == NULL) 231251881Speter *value = NULL; 232251881Speter else if (! strcmp("\n", eol)) 233251881Speter *value = "LF"; 234251881Speter else if (! strcmp("\r", eol)) 235251881Speter *value = "CR"; 236251881Speter else if (! strcmp("\r\n", eol)) 237251881Speter *value = "CRLF"; 238251881Speter else 239251881Speter *value = NULL; 240251881Speter} 241251881Speter 242251881Spetersvn_error_t * 243251881Spetersvn_wc__get_translate_info(svn_subst_eol_style_t *style, 244251881Speter const char **eol, 245251881Speter apr_hash_t **keywords, 246251881Speter svn_boolean_t *special, 247251881Speter svn_wc__db_t *db, 248251881Speter const char *local_abspath, 249251881Speter apr_hash_t *props, 250251881Speter svn_boolean_t for_normalization, 251251881Speter apr_pool_t *result_pool, 252251881Speter apr_pool_t *scratch_pool) 253251881Speter{ 254251881Speter const char *propval; 255251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 256251881Speter 257251881Speter if (props == NULL) 258251881Speter SVN_ERR(svn_wc__get_actual_props(&props, db, local_abspath, 259251881Speter scratch_pool, scratch_pool)); 260251881Speter 261251881Speter if (eol) 262251881Speter { 263251881Speter propval = svn_prop_get_value(props, SVN_PROP_EOL_STYLE); 264251881Speter 265251881Speter svn_subst_eol_style_from_value(style, eol, propval); 266251881Speter } 267251881Speter 268251881Speter if (keywords) 269251881Speter { 270251881Speter propval = svn_prop_get_value(props, SVN_PROP_KEYWORDS); 271251881Speter 272251881Speter if (!propval || *propval == '\0') 273251881Speter *keywords = NULL; 274251881Speter else 275251881Speter SVN_ERR(svn_wc__expand_keywords(keywords, 276251881Speter db, local_abspath, NULL, 277251881Speter propval, for_normalization, 278251881Speter result_pool, scratch_pool)); 279251881Speter } 280251881Speter if (special) 281251881Speter { 282251881Speter propval = svn_prop_get_value(props, SVN_PROP_SPECIAL); 283251881Speter 284251881Speter *special = (propval != NULL); 285251881Speter } 286251881Speter 287251881Speter return SVN_NO_ERROR; 288251881Speter} 289251881Speter 290251881Spetersvn_error_t * 291251881Spetersvn_wc__expand_keywords(apr_hash_t **keywords, 292251881Speter svn_wc__db_t *db, 293251881Speter const char *local_abspath, 294251881Speter const char *wri_abspath, 295251881Speter const char *keyword_list, 296251881Speter svn_boolean_t for_normalization, 297251881Speter apr_pool_t *result_pool, 298251881Speter apr_pool_t *scratch_pool) 299251881Speter{ 300251881Speter svn_revnum_t changed_rev; 301251881Speter apr_time_t changed_date; 302251881Speter const char *changed_author; 303251881Speter const char *url; 304251881Speter const char *repos_root_url; 305251881Speter 306251881Speter if (! for_normalization) 307251881Speter { 308251881Speter const char *repos_relpath; 309251881Speter 310251881Speter SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, 311251881Speter &repos_root_url, NULL, &changed_rev, 312251881Speter &changed_date, &changed_author, NULL, 313251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 314251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 315251881Speter NULL, NULL, NULL, NULL, 316251881Speter db, local_abspath, 317251881Speter scratch_pool, scratch_pool)); 318251881Speter 319299742Sdim /* Handle special statuses (e.g. added) */ 320299742Sdim if (!repos_relpath) 321299742Sdim SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath, 322299742Sdim &repos_root_url, NULL, 323299742Sdim db, local_abspath, 324299742Sdim scratch_pool, scratch_pool)); 325299742Sdim 326299742Sdim url = svn_path_url_add_component2(repos_root_url, repos_relpath, 327299742Sdim scratch_pool); 328251881Speter } 329251881Speter else 330251881Speter { 331251881Speter url = ""; 332251881Speter changed_rev = SVN_INVALID_REVNUM; 333251881Speter changed_date = 0; 334251881Speter changed_author = ""; 335251881Speter repos_root_url = ""; 336251881Speter } 337251881Speter 338251881Speter SVN_ERR(svn_subst_build_keywords3(keywords, keyword_list, 339251881Speter apr_psprintf(scratch_pool, "%ld", 340251881Speter changed_rev), 341251881Speter url, repos_root_url, 342251881Speter changed_date, changed_author, 343251881Speter result_pool)); 344251881Speter 345251881Speter if (apr_hash_count(*keywords) == 0) 346251881Speter *keywords = NULL; 347251881Speter 348251881Speter return SVN_NO_ERROR; 349251881Speter} 350251881Speter 351251881Spetersvn_error_t * 352251881Spetersvn_wc__sync_flags_with_props(svn_boolean_t *did_set, 353251881Speter svn_wc__db_t *db, 354251881Speter const char *local_abspath, 355251881Speter apr_pool_t *scratch_pool) 356251881Speter{ 357251881Speter svn_wc__db_status_t status; 358251881Speter svn_node_kind_t kind; 359251881Speter svn_wc__db_lock_t *lock; 360251881Speter apr_hash_t *props = NULL; 361251881Speter svn_boolean_t had_props; 362251881Speter svn_boolean_t props_mod; 363251881Speter 364251881Speter if (did_set) 365251881Speter *did_set = FALSE; 366251881Speter 367251881Speter /* ### We'll consolidate these info gathering statements in a future 368251881Speter commit. */ 369251881Speter 370251881Speter SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 371251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 372251881Speter NULL, &lock, NULL, NULL, NULL, NULL, NULL, 373251881Speter &had_props, &props_mod, NULL, NULL, NULL, 374251881Speter db, local_abspath, 375251881Speter scratch_pool, scratch_pool)); 376251881Speter 377251881Speter /* We actually only care about the following flags on files, so just 378251881Speter early-out for all other types. 379251881Speter 380251881Speter Also bail if there is no in-wc representation of the file. */ 381251881Speter if (kind != svn_node_file 382251881Speter || (status != svn_wc__db_status_normal 383251881Speter && status != svn_wc__db_status_added)) 384251881Speter return SVN_NO_ERROR; 385251881Speter 386251881Speter if (props_mod || had_props) 387251881Speter SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, 388251881Speter scratch_pool)); 389251881Speter else 390251881Speter props = NULL; 391251881Speter 392251881Speter /* If we get this far, we're going to change *something*, so just set 393251881Speter the flag appropriately. */ 394251881Speter if (did_set) 395251881Speter *did_set = TRUE; 396251881Speter 397251881Speter /* Handle the read-write bit. */ 398251881Speter if (status != svn_wc__db_status_normal 399251881Speter || props == NULL 400251881Speter || ! svn_hash_gets(props, SVN_PROP_NEEDS_LOCK) 401251881Speter || lock) 402251881Speter { 403251881Speter SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); 404251881Speter } 405251881Speter else 406251881Speter { 407251881Speter /* Special case: If we have an uncommitted svn:needs-lock, we don't 408251881Speter set the file read_only just yet. That happens upon commit. */ 409251881Speter apr_hash_t *pristine_props; 410251881Speter 411251881Speter if (! props_mod) 412251881Speter pristine_props = props; 413251881Speter else if (had_props) 414251881Speter SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, local_abspath, 415251881Speter scratch_pool, scratch_pool)); 416251881Speter else 417251881Speter pristine_props = NULL; 418251881Speter 419251881Speter if (pristine_props 420251881Speter && svn_hash_gets(pristine_props, SVN_PROP_NEEDS_LOCK) ) 421251881Speter /*&& props 422251881Speter && apr_hash_get(props, SVN_PROP_NEEDS_LOCK, APR_HASH_KEY_STRING) )*/ 423251881Speter SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); 424251881Speter } 425251881Speter 426251881Speter/* Windows doesn't care about the execute bit. */ 427251881Speter#ifndef WIN32 428251881Speter 429251881Speter if (props == NULL 430251881Speter || ! svn_hash_gets(props, SVN_PROP_EXECUTABLE)) 431251881Speter { 432251881Speter /* Turn off the execute bit */ 433251881Speter SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE, 434251881Speter scratch_pool)); 435251881Speter } 436251881Speter else 437251881Speter SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE, 438251881Speter scratch_pool)); 439251881Speter#endif 440251881Speter 441251881Speter return SVN_NO_ERROR; 442251881Speter} 443