1250079Scarl/* 2302484Smav * translate.c : wc-specific eol/keyword substitution 3250079Scarl * 4289542Scem * ==================================================================== 5250079Scarl * Licensed to the Apache Software Foundation (ASF) under one 6250079Scarl * or more contributor license agreements. See the NOTICE file 7250079Scarl * distributed with this work for additional information 8250079Scarl * regarding copyright ownership. The ASF licenses this file 9250079Scarl * to you under the Apache License, Version 2.0 (the 10250079Scarl * "License"); you may not use this file except in compliance 11250079Scarl * with the License. You may obtain a copy of the License at 12250079Scarl * 13250079Scarl * http://www.apache.org/licenses/LICENSE-2.0 14250079Scarl * 15250079Scarl * Unless required by applicable law or agreed to in writing, 16250079Scarl * software distributed under the License is distributed on an 17250079Scarl * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18250079Scarl * KIND, either express or implied. See the License for the 19250079Scarl * specific language governing permissions and limitations 20250079Scarl * under the License. 21250079Scarl * ==================================================================== 22250079Scarl */ 23250079Scarl 24250079Scarl 25250079Scarl 26250079Scarl#include <stdlib.h> 27250079Scarl#include <string.h> 28250079Scarl 29302484Smav#include <apr_pools.h> 30302484Smav#include <apr_file_io.h> 31302484Smav#include <apr_strings.h> 32302484Smav 33302484Smav#include "svn_types.h" 34302484Smav#include "svn_string.h" 35302484Smav#include "svn_dirent_uri.h" 36302484Smav#include "svn_hash.h" 37302484Smav#include "svn_path.h" 38302484Smav#include "svn_error.h" 39250079Scarl#include "svn_subst.h" 40250079Scarl#include "svn_io.h" 41250079Scarl#include "svn_props.h" 42250079Scarl 43250079Scarl#include "wc.h" 44250079Scarl#include "adm_files.h" 45250079Scarl#include "translate.h" 46289774Scem#include "props.h" 47302493Smav 48250079Scarl#include "svn_private_config.h" 49250079Scarl#include "private/svn_wc_private.h" 50295618Scem 51295618Scem 52250079Scarl 53250079Scarl/* */ 54289774Scemstatic svn_error_t * 55289207Scemread_handler_unsupported(void *baton, char *buffer, apr_size_t *len) 56250079Scarl{ 57250079Scarl SVN_ERR_MALFUNCTION(); 58250079Scarl} 59295618Scem 60250079Scarl/* */ 61250079Scarlstatic svn_error_t * 62250079Scarlwrite_handler_unsupported(void *baton, const char *buffer, apr_size_t *len) 63250079Scarl{ 64250079Scarl SVN_ERR_MALFUNCTION(); 65302484Smav} 66250079Scarl 67289648Scemsvn_error_t * 68250079Scarlsvn_wc__internal_translated_stream(svn_stream_t **stream, 69289539Scem svn_wc__db_t *db, 70289648Scem const char *local_abspath, 71291032Scem const char *versioned_abspath, 72250079Scarl apr_uint32_t flags, 73295618Scem apr_pool_t *result_pool, 74295618Scem apr_pool_t *scratch_pool) 75295618Scem{ 76295618Scem svn_boolean_t special; 77295618Scem svn_boolean_t to_nf = flags & SVN_WC_TRANSLATE_TO_NF; 78295618Scem svn_subst_eol_style_t style; 79295618Scem const char *eol; 80295618Scem apr_hash_t *keywords; 81295618Scem svn_boolean_t repair_forced = flags & SVN_WC_TRANSLATE_FORCE_EOL_REPAIR; 82295618Scem 83295618Scem SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 84295618Scem SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_abspath)); 85250079Scarl 86250079Scarl SVN_ERR(svn_wc__get_translate_info(&style, &eol, 87289648Scem &keywords, 88250079Scarl &special, 89250079Scarl db, versioned_abspath, NULL, FALSE, 90289610Scem scratch_pool, scratch_pool)); 91289610Scem 92289610Scem if (special) 93289610Scem { 94289610Scem if (to_nf) 95289610Scem return svn_subst_read_specialfile(stream, local_abspath, result_pool, 96289610Scem scratch_pool); 97289610Scem 98289610Scem return svn_subst_create_specialfile(stream, local_abspath, result_pool, 99289610Scem scratch_pool); 100289610Scem } 101289610Scem 102289539Scem if (to_nf) 103289539Scem SVN_ERR(svn_stream_open_readonly(stream, local_abspath, result_pool, 104289539Scem scratch_pool)); 105289539Scem else 106289539Scem { 107289539Scem apr_file_t *file; 108289539Scem 109289539Scem /* We don't want the "open-exclusively" feature of the normal 110295618Scem svn_stream_open_writable interface. Do this manually. */ 111295618Scem SVN_ERR(svn_io_file_open(&file, local_abspath, 112295618Scem APR_CREATE | APR_WRITE | APR_BUFFERED, 113295618Scem APR_OS_DEFAULT, result_pool)); 114295618Scem *stream = svn_stream_from_aprfile2(file, FALSE, result_pool); 115295618Scem } 116295618Scem 117295618Scem if (svn_subst_translation_required(style, eol, keywords, special, TRUE)) 118295618Scem { 119295618Scem if (to_nf) 120295618Scem { 121295618Scem if (style == svn_subst_eol_style_native) 122255274Scarl eol = SVN_SUBST_NATIVE_EOL_STR; 123302484Smav else if (style == svn_subst_eol_style_fixed) 124302484Smav repair_forced = TRUE; 125255274Scarl else if (style != svn_subst_eol_style_none) 126250079Scarl return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); 127250079Scarl 128255274Scarl /* Wrap the stream to translate to normal form */ 129250079Scarl *stream = svn_subst_stream_translated(*stream, 130289397Scem eol, 131250079Scarl repair_forced, 132250079Scarl keywords, 133250079Scarl FALSE /* expand */, 134250079Scarl result_pool); 135250079Scarl 136250079Scarl /* Enforce our contract. TO_NF streams are readonly */ 137250079Scarl svn_stream_set_write(*stream, write_handler_unsupported); 138250079Scarl } 139290679Scem else 140290679Scem { 141291280Scem *stream = svn_subst_stream_translated(*stream, eol, TRUE, 142289543Scem keywords, TRUE, result_pool); 143289543Scem 144289543Scem /* Enforce our contract. FROM_NF streams are write-only */ 145289543Scem svn_stream_set_read(*stream, read_handler_unsupported); 146289543Scem } 147250079Scarl } 148250079Scarl 149250079Scarl return SVN_NO_ERROR; 150250079Scarl} 151250079Scarl 152250079Scarl 153250079Scarlsvn_error_t * 154250079Scarlsvn_wc__internal_translated_file(const char **xlated_abspath, 155289546Scem const char *src_abspath, 156250079Scarl svn_wc__db_t *db, 157289546Scem const char *versioned_abspath, 158295618Scem apr_uint32_t flags, 159250079Scarl svn_cancel_func_t cancel_func, 160250079Scarl void *cancel_baton, 161289542Scem apr_pool_t *result_pool, 162289542Scem apr_pool_t *scratch_pool) 163289542Scem{ 164289542Scem svn_subst_eol_style_t style; 165289542Scem const char *eol; 166289542Scem apr_hash_t *keywords; 167289542Scem svn_boolean_t special; 168289542Scem 169289542Scem SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath)); 170289542Scem SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_abspath)); 171289542Scem SVN_ERR(svn_wc__get_translate_info(&style, &eol, 172289542Scem &keywords, 173289542Scem &special, 174289542Scem db, versioned_abspath, NULL, FALSE, 175289546Scem scratch_pool, scratch_pool)); 176289546Scem 177289546Scem if (! svn_subst_translation_required(style, eol, keywords, special, TRUE) 178289546Scem && (! (flags & SVN_WC_TRANSLATE_FORCE_COPY))) 179289546Scem { 180289546Scem /* Translation would be a no-op, so return the original file. */ 181289546Scem *xlated_abspath = src_abspath; 182289546Scem } 183289546Scem else /* some translation (or copying) is necessary */ 184289546Scem { 185289546Scem const char *tmp_dir; 186289546Scem const char *tmp_vfile; 187289542Scem svn_boolean_t repair_forced 188289542Scem = (flags & SVN_WC_TRANSLATE_FORCE_EOL_REPAIR) != 0; 189289542Scem svn_boolean_t expand = (flags & SVN_WC_TRANSLATE_TO_NF) == 0; 190289542Scem 191289542Scem if (flags & SVN_WC_TRANSLATE_USE_GLOBAL_TMP) 192289542Scem tmp_dir = NULL; 193289542Scem else 194289542Scem SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir, db, versioned_abspath, 195289542Scem scratch_pool, scratch_pool)); 196289542Scem 197295618Scem SVN_ERR(svn_io_open_unique_file3(NULL, &tmp_vfile, tmp_dir, 198295618Scem (flags & SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP) 199295618Scem ? svn_io_file_del_none 200295618Scem : svn_io_file_del_on_pool_cleanup, 201295618Scem result_pool, scratch_pool)); 202250079Scarl 203303429Smav /* ### ugh. the repair behavior does NOT match the docstring. bleah. 204303429Smav ### all of these translation functions are crap and should go 205303429Smav ### away anyways. we'll just deprecate most of the functions and 206250079Scarl ### properly document the survivors */ 207250079Scarl 208289774Scem if (expand) 209250079Scarl { 210250079Scarl /* from normal form */ 211250079Scarl 212250079Scarl repair_forced = TRUE; 213250079Scarl } 214295618Scem else 215295618Scem { 216295618Scem /* to normal form */ 217295618Scem 218295618Scem if (style == svn_subst_eol_style_native) 219295618Scem eol = SVN_SUBST_NATIVE_EOL_STR; 220295618Scem else if (style == svn_subst_eol_style_fixed) 221250079Scarl repair_forced = TRUE; 222250079Scarl else if (style != svn_subst_eol_style_none) 223250079Scarl return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL); 224289546Scem } 225250079Scarl 226289610Scem SVN_ERR(svn_subst_copy_and_translate4(src_abspath, tmp_vfile, 227289610Scem eol, repair_forced, 228289610Scem keywords, 229289539Scem expand, 230289542Scem special, 231289542Scem cancel_func, cancel_baton, 232289542Scem result_pool)); 233289543Scem 234289542Scem *xlated_abspath = tmp_vfile; 235301293Smav } 236295618Scem 237289542Scem return SVN_NO_ERROR; 238289539Scem} 239289539Scem 240289539Scemvoid 241289539Scemsvn_wc__eol_value_from_string(const char **value, const char *eol) 242289539Scem{ 243289542Scem if (eol == NULL) 244289546Scem *value = NULL; 245289546Scem else if (! strcmp("\n", eol)) 246289546Scem *value = "LF"; 247289546Scem else if (! strcmp("\r", eol)) 248289542Scem *value = "CR"; 249289542Scem else if (! strcmp("\r\n", eol)) 250290686Scem *value = "CRLF"; 251290686Scem else 252289542Scem *value = NULL; 253289542Scem} 254289542Scem 255289546Scemsvn_error_t * 256322980Smavsvn_wc__get_translate_info(svn_subst_eol_style_t *style, 257322980Smav const char **eol, 258289542Scem apr_hash_t **keywords, 259289542Scem svn_boolean_t *special, 260289542Scem svn_wc__db_t *db, 261289542Scem const char *local_abspath, 262289542Scem apr_hash_t *props, 263289542Scem svn_boolean_t for_normalization, 264289542Scem apr_pool_t *result_pool, 265250079Scarl apr_pool_t *scratch_pool) 266250079Scarl{ 267289234Scem const char *propval; 268289234Scem SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 269289234Scem 270289234Scem if (props == NULL) 271289234Scem SVN_ERR(svn_wc__get_actual_props(&props, db, local_abspath, 272289234Scem scratch_pool, scratch_pool)); 273289234Scem 274289234Scem if (eol) 275289234Scem { 276289234Scem propval = svn_prop_get_value(props, SVN_PROP_EOL_STYLE); 277289234Scem 278289234Scem svn_subst_eol_style_from_value(style, eol, propval); 279289234Scem } 280289234Scem 281289234Scem if (keywords) 282289234Scem { 283289234Scem propval = svn_prop_get_value(props, SVN_PROP_KEYWORDS); 284289234Scem 285289234Scem if (!propval || *propval == '\0') 286289234Scem *keywords = NULL; 287303429Smav else 288255279Scarl SVN_ERR(svn_wc__expand_keywords(keywords, 289255279Scarl db, local_abspath, NULL, 290303429Smav propval, for_normalization, 291255279Scarl result_pool, scratch_pool)); 292255279Scarl } 293303429Smav if (special) 294303429Smav { 295303429Smav propval = svn_prop_get_value(props, SVN_PROP_SPECIAL); 296303429Smav 297303429Smav *special = (propval != NULL); 298303429Smav } 299303429Smav 300303429Smav return SVN_NO_ERROR; 301303429Smav} 302289397Scem 303250079Scarlsvn_error_t * 304303429Smavsvn_wc__expand_keywords(apr_hash_t **keywords, 305303429Smav svn_wc__db_t *db, 306303429Smav const char *local_abspath, 307303429Smav const char *wri_abspath, 308303429Smav const char *keyword_list, 309303429Smav svn_boolean_t for_normalization, 310303429Smav apr_pool_t *result_pool, 311302484Smav apr_pool_t *scratch_pool) 312303429Smav{ 313302484Smav svn_revnum_t changed_rev; 314303429Smav apr_time_t changed_date; 315303429Smav const char *changed_author; 316303429Smav const char *url; 317302484Smav const char *repos_root_url; 318303429Smav 319303429Smav if (! for_normalization) 320289546Scem { 321289546Scem const char *repos_relpath; 322289546Scem 323303429Smav SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, 324303429Smav &repos_root_url, NULL, &changed_rev, 325291280Scem &changed_date, &changed_author, NULL, 326289647Scem NULL, NULL, NULL, NULL, NULL, NULL, 327289647Scem NULL, NULL, NULL, NULL, NULL, NULL, NULL, 328255272Scarl NULL, NULL, NULL, NULL, 329255272Scarl db, local_abspath, 330255272Scarl scratch_pool, scratch_pool)); 331303429Smav 332303429Smav if (repos_relpath) 333303429Smav url = svn_path_url_add_component2(repos_root_url, repos_relpath, 334303429Smav scratch_pool); 335303429Smav else 336303429Smav SVN_ERR(svn_wc__db_read_url(&url, db, local_abspath, scratch_pool, 337303429Smav scratch_pool)); 338303429Smav } 339289546Scem else 340289546Scem { 341289546Scem url = ""; 342290678Scem changed_rev = SVN_INVALID_REVNUM; 343290678Scem changed_date = 0; 344303429Smav changed_author = ""; 345303429Smav repos_root_url = ""; 346303429Smav } 347303429Smav 348303429Smav SVN_ERR(svn_subst_build_keywords3(keywords, keyword_list, 349303429Smav apr_psprintf(scratch_pool, "%ld", 350303429Smav changed_rev), 351303429Smav url, repos_root_url, 352303429Smav changed_date, changed_author, 353303429Smav result_pool)); 354303429Smav 355289648Scem if (apr_hash_count(*keywords) == 0) 356289543Scem *keywords = NULL; 357289543Scem 358289543Scem return SVN_NO_ERROR; 359289543Scem} 360289543Scem 361289543Scemsvn_error_t * 362289542Scemsvn_wc__sync_flags_with_props(svn_boolean_t *did_set, 363289542Scem svn_wc__db_t *db, 364289546Scem const char *local_abspath, 365295618Scem apr_pool_t *scratch_pool) 366289648Scem{ 367303429Smav svn_wc__db_status_t status; 368303429Smav svn_node_kind_t kind; 369289648Scem svn_wc__db_lock_t *lock; 370289648Scem apr_hash_t *props = NULL; 371303429Smav svn_boolean_t had_props; 372255274Scarl svn_boolean_t props_mod; 373303429Smav 374289774Scem if (did_set) 375300100Scem *did_set = FALSE; 376300100Scem 377289774Scem /* ### We'll consolidate these info gathering statements in a future 378289774Scem commit. */ 379250079Scarl 380290685Scem SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, 381290685Scem NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 382290685Scem NULL, &lock, NULL, NULL, NULL, NULL, NULL, 383303429Smav &had_props, &props_mod, NULL, NULL, NULL, 384290685Scem db, local_abspath, 385290685Scem scratch_pool, scratch_pool)); 386290685Scem 387290685Scem /* We actually only care about the following flags on files, so just 388290685Scem early-out for all other types. 389295486Scem 390295486Scem Also bail if there is no in-wc representation of the file. */ 391295486Scem if (kind != svn_node_file 392295486Scem || (status != svn_wc__db_status_normal 393295486Scem && status != svn_wc__db_status_added)) 394295486Scem return SVN_NO_ERROR; 395295486Scem 396295486Scem if (props_mod || had_props) 397295486Scem SVN_ERR(svn_wc__db_read_props(&props, db, local_abspath, scratch_pool, 398295486Scem scratch_pool)); 399295486Scem else 400295486Scem props = NULL; 401295486Scem 402295486Scem /* If we get this far, we're going to change *something*, so just set 403295486Scem the flag appropriately. */ 404291030Scem if (did_set) 405295486Scem *did_set = TRUE; 406303429Smav 407295486Scem /* Handle the read-write bit. */ 408295486Scem if (status != svn_wc__db_status_normal 409295486Scem || props == NULL 410295486Scem || ! svn_hash_gets(props, SVN_PROP_NEEDS_LOCK) 411295486Scem || lock) 412295486Scem { 413295486Scem SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool)); 414295486Scem } 415295486Scem else 416295486Scem { 417295486Scem /* Special case: If we have an uncommitted svn:needs-lock, we don't 418295486Scem set the file read_only just yet. That happens upon commit. */ 419295486Scem apr_hash_t *pristine_props; 420295486Scem 421295486Scem if (! props_mod) 422295486Scem pristine_props = props; 423295486Scem else if (had_props) 424295486Scem SVN_ERR(svn_wc__db_read_pristine_props(&pristine_props, db, local_abspath, 425295486Scem scratch_pool, scratch_pool)); 426295486Scem else 427295487Scem pristine_props = NULL; 428295487Scem 429295487Scem if (pristine_props 430295487Scem && svn_hash_gets(pristine_props, SVN_PROP_NEEDS_LOCK) ) 431295487Scem /*&& props 432303429Smav && apr_hash_get(props, SVN_PROP_NEEDS_LOCK, APR_HASH_KEY_STRING) )*/ 433295487Scem SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool)); 434295487Scem } 435295487Scem 436295487Scem/* Windows doesn't care about the execute bit. */ 437295487Scem#ifndef WIN32 438295487Scem 439295487Scem if (props == NULL 440295487Scem || ! svn_hash_gets(props, SVN_PROP_EXECUTABLE)) 441295487Scem { 442295487Scem /* Turn off the execute bit */ 443295487Scem SVN_ERR(svn_io_set_file_executable(local_abspath, FALSE, FALSE, 444295487Scem scratch_pool)); 445295487Scem } 446295487Scem else 447295487Scem SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE, 448295487Scem scratch_pool)); 449295487Scem#endif 450295487Scem 451295487Scem return SVN_NO_ERROR; 452295487Scem} 453302508Smav