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