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