1289177Speter/*
2289177Speter * export-cmd.c -- Subversion export command
3289177Speter *
4289177Speter * ====================================================================
5289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
6289177Speter *    or more contributor license agreements.  See the NOTICE file
7289177Speter *    distributed with this work for additional information
8289177Speter *    regarding copyright ownership.  The ASF licenses this file
9289177Speter *    to you under the Apache License, Version 2.0 (the
10289177Speter *    "License"); you may not use this file except in compliance
11289177Speter *    with the License.  You may obtain a copy of the License at
12289177Speter *
13289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
14289177Speter *
15289177Speter *    Unless required by applicable law or agreed to in writing,
16289177Speter *    software distributed under the License is distributed on an
17289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18289177Speter *    KIND, either express or implied.  See the License for the
19289177Speter *    specific language governing permissions and limitations
20289177Speter *    under the License.
21289177Speter * ====================================================================
22289177Speter */
23289177Speter
24289177Speter/* ==================================================================== */
25289177Speter
26289177Speter
27289177Speter
28289177Speter/*** Includes. ***/
29289177Speter
30289177Speter#include "svn_client.h"
31289177Speter#include "svn_error.h"
32289177Speter#include "svn_dirent_uri.h"
33289177Speter#include "svn_path.h"
34289177Speter#include "svn_cmdline.h"
35289177Speter#include "cl.h"
36289177Speter
37289177Speter#include "svn_private_config.h"
38289177Speter#include "private/svn_string_private.h"
39289177Speter#include "private/svn_client_private.h"
40289177Speter
41289177Speter/*** The export editor code. ***/
42289177Speter
43289177Speter/* ---------------------------------------------------------------------- */
44289177Speter
45289177Speter/*** A dedicated 'export' editor, which does no .svn/ accounting.  ***/
46289177Speter
47289177Spetertypedef struct edit_baton_t
48289177Speter{
49289177Speter  apr_int64_t file_count;
50289177Speter  apr_int64_t dir_count;
51289177Speter  apr_int64_t byte_count;
52289177Speter  apr_int64_t prop_count;
53289177Speter  apr_int64_t prop_byte_count;
54289177Speter} edit_baton_t;
55289177Speter
56289177Speterstatic svn_error_t *
57289177Speterset_target_revision(void *edit_baton,
58289177Speter                    svn_revnum_t target_revision,
59289177Speter                    apr_pool_t *pool)
60289177Speter{
61289177Speter  return SVN_NO_ERROR;
62289177Speter}
63289177Speter
64289177Speter
65289177Speter/* Just ensure that the main export directory exists. */
66289177Speterstatic svn_error_t *
67289177Speteropen_root(void *edit_baton,
68289177Speter          svn_revnum_t base_revision,
69289177Speter          apr_pool_t *pool,
70289177Speter          void **root_baton)
71289177Speter{
72289177Speter  *root_baton = edit_baton;
73289177Speter  return SVN_NO_ERROR;
74289177Speter}
75289177Speter
76289177Speter
77289177Speter/* Ensure the directory exists, and send feedback. */
78289177Speterstatic svn_error_t *
79289177Speteradd_directory(const char *path,
80289177Speter              void *parent_baton,
81289177Speter              const char *copyfrom_path,
82289177Speter              svn_revnum_t copyfrom_revision,
83289177Speter              apr_pool_t *pool,
84289177Speter              void **baton)
85289177Speter{
86289177Speter  edit_baton_t *eb = parent_baton;
87289177Speter  eb->dir_count++;
88289177Speter
89289177Speter  *baton = parent_baton;
90289177Speter  return SVN_NO_ERROR;
91289177Speter}
92289177Speter
93289177Speter
94289177Speter/* Build a file baton. */
95289177Speterstatic svn_error_t *
96289177Speteradd_file(const char *path,
97289177Speter          void *parent_baton,
98289177Speter          const char *copyfrom_path,
99289177Speter          svn_revnum_t copyfrom_revision,
100289177Speter          apr_pool_t *pool,
101289177Speter          void **baton)
102289177Speter{
103289177Speter  edit_baton_t *eb = parent_baton;
104289177Speter  eb->file_count++;
105289177Speter
106289177Speter  *baton = parent_baton;
107289177Speter  return SVN_NO_ERROR;
108289177Speter}
109289177Speter
110289177Speterstatic svn_error_t *
111289177Speterwindow_handler(svn_txdelta_window_t *window, void *baton)
112289177Speter{
113289177Speter  edit_baton_t *eb = baton;
114289177Speter  if (window != NULL)
115289177Speter    eb->byte_count += window->tview_len;
116289177Speter
117289177Speter  return SVN_NO_ERROR;
118289177Speter}
119289177Speter
120289177Speter/* Write incoming data into the tmpfile stream */
121289177Speter
122289177Speterstatic svn_error_t *
123289177Speterapply_textdelta(void *file_baton,
124289177Speter                const char *base_checksum,
125289177Speter                apr_pool_t *pool,
126289177Speter                svn_txdelta_window_handler_t *handler,
127289177Speter                void **handler_baton)
128289177Speter{
129289177Speter  *handler_baton = file_baton;
130289177Speter  *handler = window_handler;
131289177Speter
132289177Speter  return SVN_NO_ERROR;
133289177Speter}
134289177Speter
135289177Speterstatic svn_error_t *
136289177Speterchange_file_prop(void *file_baton,
137289177Speter                 const char *name,
138289177Speter                 const svn_string_t *value,
139289177Speter                 apr_pool_t *pool)
140289177Speter{
141289177Speter  edit_baton_t *eb = file_baton;
142289177Speter  eb->prop_count++;
143289177Speter  eb->prop_byte_count += value->len;
144289177Speter
145289177Speter  return SVN_NO_ERROR;
146289177Speter}
147289177Speter
148289177Speterstatic svn_error_t *
149289177Speterchange_dir_prop(void *dir_baton,
150289177Speter                const char *name,
151289177Speter                const svn_string_t *value,
152289177Speter                apr_pool_t *pool)
153289177Speter{
154289177Speter  edit_baton_t *eb = dir_baton;
155289177Speter  eb->prop_count++;
156289177Speter
157289177Speter  return SVN_NO_ERROR;
158289177Speter}
159289177Speter
160289177Speterstatic svn_error_t *
161289177Speterclose_file(void *file_baton,
162289177Speter           const char *text_checksum,
163289177Speter           apr_pool_t *pool)
164289177Speter{
165289177Speter  return SVN_NO_ERROR;
166289177Speter}
167289177Speter
168289177Speter
169289177Speter/*** Public Interfaces ***/
170289177Speter
171289177Speterstatic svn_error_t *
172289177Speterbench_null_export(svn_revnum_t *result_rev,
173289177Speter                  const char *from_path_or_url,
174289177Speter                  svn_opt_revision_t *peg_revision,
175289177Speter                  svn_opt_revision_t *revision,
176289177Speter                  svn_depth_t depth,
177289177Speter                  void *baton,
178289177Speter                  svn_client_ctx_t *ctx,
179289177Speter                  svn_boolean_t quiet,
180289177Speter                  apr_pool_t *pool)
181289177Speter{
182289177Speter  svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
183289177Speter  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
184289177Speter
185289177Speter  SVN_ERR_ASSERT(peg_revision != NULL);
186289177Speter  SVN_ERR_ASSERT(revision != NULL);
187289177Speter
188289177Speter  if (peg_revision->kind == svn_opt_revision_unspecified)
189289177Speter    peg_revision->kind = svn_path_is_url(from_path_or_url)
190289177Speter                       ? svn_opt_revision_head
191289177Speter                       : svn_opt_revision_working;
192289177Speter
193289177Speter  if (revision->kind == svn_opt_revision_unspecified)
194289177Speter    revision = peg_revision;
195289177Speter
196289177Speter  if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
197289177Speter    {
198289177Speter      svn_client__pathrev_t *loc;
199289177Speter      svn_ra_session_t *ra_session;
200289177Speter      svn_node_kind_t kind;
201289177Speter
202289177Speter      /* Get the RA connection. */
203289177Speter      SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
204289177Speter                                                from_path_or_url, NULL,
205289177Speter                                                peg_revision,
206289177Speter                                                revision, ctx, pool));
207289177Speter
208289177Speter      SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool));
209289177Speter
210289177Speter      if (kind == svn_node_file)
211289177Speter        {
212289177Speter          apr_hash_t *props;
213289177Speter
214289177Speter          /* Since you cannot actually root an editor at a file, we
215289177Speter           * manually drive a few functions of our editor. */
216289177Speter
217289177Speter          /* Step outside the editor-likeness for a moment, to actually talk
218289177Speter           * to the repository. */
219289177Speter          /* ### note: the stream will not be closed */
220289177Speter          SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
221289177Speter                                  svn_stream_empty(pool),
222289177Speter                                  NULL, &props, pool));
223289177Speter        }
224289177Speter      else if (kind == svn_node_dir)
225289177Speter        {
226289177Speter          void *edit_baton = NULL;
227289177Speter          const svn_delta_editor_t *export_editor = NULL;
228289177Speter          const svn_ra_reporter3_t *reporter;
229289177Speter          void *report_baton;
230289177Speter
231289177Speter          svn_delta_editor_t *editor = svn_delta_default_editor(pool);
232289177Speter
233289177Speter          editor->set_target_revision = set_target_revision;
234289177Speter          editor->open_root = open_root;
235289177Speter          editor->add_directory = add_directory;
236289177Speter          editor->add_file = add_file;
237289177Speter          editor->apply_textdelta = apply_textdelta;
238289177Speter          editor->close_file = close_file;
239289177Speter          editor->change_file_prop = change_file_prop;
240289177Speter          editor->change_dir_prop = change_dir_prop;
241289177Speter
242289177Speter          /* for ra_svn, we don't need an editior in quiet mode */
243289177Speter          if (!quiet || strncmp(loc->repos_root_url, "svn:", 4))
244289177Speter            SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func,
245289177Speter                                                      ctx->cancel_baton,
246289177Speter                                                      editor,
247289177Speter                                                      baton,
248289177Speter                                                      &export_editor,
249289177Speter                                                      &edit_baton,
250289177Speter                                                      pool));
251289177Speter
252289177Speter          /* Manufacture a basic 'report' to the update reporter. */
253289177Speter          SVN_ERR(svn_ra_do_update3(ra_session,
254289177Speter                                    &reporter, &report_baton,
255289177Speter                                    loc->rev,
256289177Speter                                    "", /* no sub-target */
257289177Speter                                    depth,
258289177Speter                                    FALSE, /* don't want copyfrom-args */
259289177Speter                                    FALSE, /* don't want ignore_ancestry */
260289177Speter                                    export_editor, edit_baton,
261289177Speter                                    pool, pool));
262289177Speter
263289177Speter          SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
264289177Speter                                     /* Depth is irrelevant, as we're
265289177Speter                                        passing start_empty=TRUE anyway. */
266289177Speter                                     svn_depth_infinity,
267289177Speter                                     TRUE, /* "help, my dir is empty!" */
268289177Speter                                     NULL, pool));
269289177Speter
270289177Speter          SVN_ERR(reporter->finish_report(report_baton, pool));
271289177Speter        }
272289177Speter      else if (kind == svn_node_none)
273289177Speter        {
274289177Speter          return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
275289177Speter                                   _("URL '%s' doesn't exist"),
276289177Speter                                   from_path_or_url);
277289177Speter        }
278289177Speter      /* kind == svn_node_unknown not handled */
279289177Speter    }
280289177Speter
281289177Speter
282289177Speter  if (result_rev)
283289177Speter    *result_rev = edit_revision;
284289177Speter
285289177Speter  return SVN_NO_ERROR;
286289177Speter}
287289177Speter
288289177Speter
289289177Speter/*** Code. ***/
290289177Speter
291289177Speter/* This implements the `svn_opt_subcommand_t' interface. */
292289177Spetersvn_error_t *
293289177Spetersvn_cl__null_export(apr_getopt_t *os,
294289177Speter                    void *baton,
295289177Speter                    apr_pool_t *pool)
296289177Speter{
297289177Speter  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
298289177Speter  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
299289177Speter  const char *from;
300289177Speter  apr_array_header_t *targets;
301289177Speter  svn_error_t *err;
302289177Speter  svn_opt_revision_t peg_revision;
303289177Speter  const char *truefrom;
304289177Speter  edit_baton_t eb = { 0 };
305289177Speter
306289177Speter  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
307289177Speter                                                      opt_state->targets,
308289177Speter                                                      ctx, FALSE, pool));
309289177Speter
310289177Speter  /* We want exactly 1 or 2 targets for this subcommand. */
311289177Speter  if (targets->nelts < 1)
312289177Speter    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
313289177Speter  if (targets->nelts > 2)
314289177Speter    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
315289177Speter
316289177Speter  /* The first target is the `from' path. */
317289177Speter  from = APR_ARRAY_IDX(targets, 0, const char *);
318289177Speter
319289177Speter  /* Get the peg revision if present. */
320289177Speter  SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool));
321289177Speter
322289177Speter  if (opt_state->depth == svn_depth_unknown)
323289177Speter    opt_state->depth = svn_depth_infinity;
324289177Speter
325289177Speter  /* Do the export. */
326289177Speter  err = bench_null_export(NULL, truefrom, &peg_revision,
327289177Speter                          &(opt_state->start_revision),
328289177Speter                          opt_state->depth,
329289177Speter                          &eb,
330289177Speter                          ctx, opt_state->quiet, pool);
331289177Speter
332289177Speter  if (!opt_state->quiet)
333289177Speter    SVN_ERR(svn_cmdline_printf(pool,
334289177Speter                               _("%15s directories\n"
335289177Speter                                 "%15s files\n"
336289177Speter                                 "%15s bytes in files\n"
337289177Speter                                 "%15s properties\n"
338289177Speter                                 "%15s bytes in properties\n"),
339289177Speter                               svn__ui64toa_sep(eb.dir_count, ',', pool),
340289177Speter                               svn__ui64toa_sep(eb.file_count, ',', pool),
341289177Speter                               svn__ui64toa_sep(eb.byte_count, ',', pool),
342289177Speter                               svn__ui64toa_sep(eb.prop_count, ',', pool),
343289177Speter                               svn__ui64toa_sep(eb.prop_byte_count, ',', pool)));
344289177Speter
345289177Speter  return svn_error_trace(err);
346289177Speter}
347