]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svn/mergeinfo-cmd.c
Backport security fix for absolute path traversal vulnerability in bsdcpio.
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svn / mergeinfo-cmd.c
1 /*
2  * mergeinfo-cmd.c -- Query merge-relative info.
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include "svn_compat.h"
31 #include "svn_pools.h"
32 #include "svn_props.h"
33 #include "svn_client.h"
34 #include "svn_cmdline.h"
35 #include "svn_path.h"
36 #include "svn_error.h"
37 #include "svn_error_codes.h"
38 #include "svn_types.h"
39 #include "cl.h"
40 #include "cl-log.h"
41
42 #include "svn_private_config.h"
43
44 \f
45 /*** Code. ***/
46
47 /* Implements the svn_log_entry_receiver_t interface. */
48 static svn_error_t *
49 print_log_rev(void *baton,
50               svn_log_entry_t *log_entry,
51               apr_pool_t *pool)
52 {
53   if (log_entry->non_inheritable)
54     SVN_ERR(svn_cmdline_printf(pool, "r%ld*\n", log_entry->revision));
55   else
56     SVN_ERR(svn_cmdline_printf(pool, "r%ld\n", log_entry->revision));
57
58   return SVN_NO_ERROR;
59 }
60
61 /* Implements a svn_log_entry_receiver_t interface that filters out changed
62  * paths data before calling the svn_cl__log_entry_receiver().  Right now we
63  * always have to pass TRUE for discover_changed_paths for
64  * svn_client_mergeinfo_log2() due to the side effect of that option.  The
65  * svn_cl__log_entry_receiver() discovers if it should print the changed paths
66  * implicitly by the path info existing.  As a result this filter is needed
67  * to allow expected output without changed paths.
68  */
69 static svn_error_t *
70 print_log_details(void *baton,
71                   svn_log_entry_t *log_entry,
72                   apr_pool_t *pool)
73 {
74   log_entry->changed_paths = NULL;
75   log_entry->changed_paths2 = NULL;
76
77   return svn_cl__log_entry_receiver(baton, log_entry, pool);
78 }
79
80 /* Draw a diagram (by printing text to the console) summarizing the state
81  * of merging between two branches, given the merge description
82  * indicated by YCA, BASE, RIGHT, TARGET, REINTEGRATE_LIKE. */
83 static svn_error_t *
84 mergeinfo_diagram(const char *yca_url,
85                   const char *base_url,
86                   const char *right_url,
87                   const char *target_url,
88                   svn_revnum_t yca_rev,
89                   svn_revnum_t base_rev,
90                   svn_revnum_t right_rev,
91                   svn_revnum_t target_rev,
92                   const char *repos_root_url,
93                   svn_boolean_t target_is_wc,
94                   svn_boolean_t reintegrate_like,
95                   apr_pool_t *pool)
96 {
97   /* The graph occupies 4 rows of text, and the annotations occupy
98    * another 2 rows above and 2 rows below.  The graph is constructed
99    * from left to right in discrete sections ("columns"), each of which
100    * can have a different width (measured in characters).  Each element in
101    * the array is either a text string of the appropriate width, or can
102    * be NULL to draw a blank cell. */
103 #define ROWS 8
104 #define COLS 4
105   const char *g[ROWS][COLS] = {{0}};
106   int col_width[COLS];
107   int row, col;
108
109   /* The YCA (that is, the branching point).  And an ellipsis, because we
110    * don't show information about earlier merges */
111   g[0][0] = apr_psprintf(pool, "  %-8ld  ", yca_rev);
112   g[1][0] =     "  |         ";
113   if (strcmp(yca_url, right_url) == 0)
114     {
115       g[2][0] = "-------| |--";
116       g[3][0] = "   \\        ";
117       g[4][0] = "    \\       ";
118       g[5][0] = "     --| |--";
119     }
120   else if (strcmp(yca_url, target_url) == 0)
121     {
122       g[2][0] = "     --| |--";
123       g[3][0] = "    /       ";
124       g[4][0] = "   /        ";
125       g[5][0] = "-------| |--";
126     }
127   else
128     {
129       g[2][0] = "     --| |--";
130       g[3][0] = "... /       ";
131       g[4][0] = "    \\       ";
132       g[5][0] = "     --| |--";
133     }
134
135   /* The last full merge */
136   if ((base_rev > yca_rev) && reintegrate_like)
137     {
138       g[2][2] = "---------";
139       g[3][2] = "  /      ";
140       g[4][2] = " /       ";
141       g[5][2] = "---------";
142       g[6][2] = "|        ";
143       g[7][2] = apr_psprintf(pool, "%-8ld ", base_rev);
144     }
145   else if (base_rev > yca_rev)
146     {
147       g[0][2] = apr_psprintf(pool, "%-8ld ", base_rev);
148       g[1][2] = "|        ";
149       g[2][2] = "---------";
150       g[3][2] = " \\       ";
151       g[4][2] = "  \\      ";
152       g[5][2] = "---------";
153     }
154   else
155     {
156       g[2][2] = "---------";
157       g[3][2] = "         ";
158       g[4][2] = "         ";
159       g[5][2] = "---------";
160     }
161
162   /* The tips of the branches */
163     {
164       g[0][3] = apr_psprintf(pool, "%-8ld", right_rev);
165       g[1][3] = "|       ";
166       g[2][3] = "-       ";
167       g[3][3] = "        ";
168       g[4][3] = "        ";
169       g[5][3] = "-       ";
170       g[6][3] = "|       ";
171       g[7][3] = target_is_wc ? "WC      "
172                              : apr_psprintf(pool, "%-8ld", target_rev);
173     }
174
175   /* Find the width of each column, so we know how to print blank cells */
176   for (col = 0; col < COLS; col++)
177     {
178       col_width[col] = 0;
179       for (row = 0; row < ROWS; row++)
180         {
181           if (g[row][col] && ((int)strlen(g[row][col]) > col_width[col]))
182             col_width[col] = (int)strlen(g[row][col]);
183         }
184     }
185
186   /* Column headings */
187   SVN_ERR(svn_cmdline_printf(pool,
188             "    %s\n"
189             "    |         %s\n"
190             "    |         |        %s\n"
191             "    |         |        |         %s\n"
192             "\n",
193             _("youngest common ancestor"), _("last full merge"),
194             _("tip of branch"), _("repository path")));
195
196   /* Print the diagram, row by row */
197   for (row = 0; row < ROWS; row++)
198     {
199       SVN_ERR(svn_cmdline_fputs("  ", stdout, pool));
200       for (col = 0; col < COLS; col++)
201         {
202           if (g[row][col])
203             {
204               SVN_ERR(svn_cmdline_fputs(g[row][col], stdout, pool));
205             }
206           else
207             {
208               /* Print <column-width> spaces */
209               SVN_ERR(svn_cmdline_printf(pool, "%*s", col_width[col], ""));
210             }
211         }
212       if (row == 2)
213         SVN_ERR(svn_cmdline_printf(pool, "  %s",
214                 svn_uri_skip_ancestor(repos_root_url, right_url, pool)));
215       if (row == 5)
216         SVN_ERR(svn_cmdline_printf(pool, "  %s",
217                 svn_uri_skip_ancestor(repos_root_url, target_url, pool)));
218       SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
219     }
220
221   return SVN_NO_ERROR;
222 }
223
224 /* Display a summary of the state of merging between the two branches
225  * SOURCE_PATH_OR_URL@SOURCE_REVISION and
226  * TARGET_PATH_OR_URL@TARGET_REVISION. */
227 static svn_error_t *
228 mergeinfo_summary(
229                   const char *source_path_or_url,
230                   const svn_opt_revision_t *source_revision,
231                   const char *target_path_or_url,
232                   const svn_opt_revision_t *target_revision,
233                   svn_client_ctx_t *ctx,
234                   apr_pool_t *pool)
235 {
236   const char *yca_url, *base_url, *right_url, *target_url;
237   svn_revnum_t yca_rev, base_rev, right_rev, target_rev;
238   const char *repos_root_url;
239   svn_boolean_t target_is_wc, is_reintegration;
240
241   target_is_wc = (! svn_path_is_url(target_path_or_url))
242                  && (target_revision->kind == svn_opt_revision_unspecified
243                      || target_revision->kind == svn_opt_revision_working
244                      || target_revision->kind == svn_opt_revision_base);
245   SVN_ERR(svn_client_get_merging_summary(
246             &is_reintegration,
247             &yca_url, &yca_rev,
248             &base_url, &base_rev,
249             &right_url, &right_rev,
250             &target_url, &target_rev,
251             &repos_root_url,
252             source_path_or_url, source_revision,
253             target_path_or_url, target_revision,
254             ctx, pool, pool));
255
256   SVN_ERR(mergeinfo_diagram(yca_url, base_url, right_url, target_url,
257                             yca_rev, base_rev, right_rev, target_rev,
258                             repos_root_url, target_is_wc, is_reintegration,
259                             pool));
260
261   return SVN_NO_ERROR;
262 }
263
264 static svn_error_t *
265 mergeinfo_log(svn_boolean_t finding_merged,
266               const char *target,
267               const svn_opt_revision_t *tgt_peg_revision,
268               const char *source,
269               const svn_opt_revision_t *src_peg_revision,
270               const svn_opt_revision_t *src_start_revision,
271               const svn_opt_revision_t *src_end_revision,
272               svn_depth_t depth,
273               svn_boolean_t include_log_details,
274               svn_boolean_t quiet,
275               svn_boolean_t verbose,
276               svn_boolean_t incremental,
277               svn_client_ctx_t *ctx,
278               apr_pool_t *pool)
279 {
280   apr_array_header_t *revprops;
281   svn_log_entry_receiver_t log_receiver;
282   void *log_receiver_baton;
283
284   if (include_log_details)
285     {
286       svn_cl__log_receiver_baton *baton;
287
288       revprops = apr_array_make(pool, 3, sizeof(const char *));
289       APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_AUTHOR;
290       APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_DATE;
291       if (!quiet)
292         APR_ARRAY_PUSH(revprops, const char *) = SVN_PROP_REVISION_LOG;
293
294       if (verbose)
295         log_receiver = svn_cl__log_entry_receiver;
296       else
297         log_receiver = print_log_details;
298
299       baton = apr_palloc(pool, sizeof(svn_cl__log_receiver_baton));
300       baton->ctx = ctx;
301       baton->target_path_or_url = target;
302       baton->target_peg_revision = *tgt_peg_revision;
303       baton->omit_log_message = quiet;
304       baton->show_diff = FALSE;
305       baton->depth = depth;
306       baton->diff_extensions = NULL;
307       baton->merge_stack = NULL;
308       baton->search_patterns = NULL;
309       baton->pool = pool;
310       log_receiver_baton = baton;
311     }
312   else
313     {
314       /* We need only revisions number, not revision properties. */
315       revprops = apr_array_make(pool, 0, sizeof(const char *));
316       log_receiver = print_log_rev;
317       log_receiver_baton = NULL;
318     }
319
320   SVN_ERR(svn_client_mergeinfo_log2(finding_merged, target,
321                                     tgt_peg_revision,
322                                     source, src_peg_revision,
323                                     src_start_revision,
324                                     src_end_revision,
325                                     log_receiver, log_receiver_baton,
326                                     TRUE, depth, revprops, ctx,
327                                     pool));
328
329   if (include_log_details && !incremental)
330     SVN_ERR(svn_cmdline_printf(pool, SVN_CL__LOG_SEP_STRING));
331
332   return SVN_NO_ERROR;
333 }
334
335 /* This implements the `svn_opt_subcommand_t' interface. */
336 svn_error_t *
337 svn_cl__mergeinfo(apr_getopt_t *os,
338                   void *baton,
339                   apr_pool_t *pool)
340 {
341   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
342   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
343   apr_array_header_t *targets;
344   const char *source, *target;
345   svn_opt_revision_t src_peg_revision, tgt_peg_revision;
346   svn_opt_revision_t *src_start_revision, *src_end_revision;
347   /* Default to depth empty. */
348   svn_depth_t depth = (opt_state->depth == svn_depth_unknown)
349                       ? svn_depth_empty : opt_state->depth;
350
351   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
352                                                       opt_state->targets,
353                                                       ctx, FALSE, pool));
354
355   /* Parse the arguments: SOURCE[@REV] optionally followed by TARGET[@REV]. */
356   if (targets->nelts < 1)
357     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
358                             _("Not enough arguments given"));
359   if (targets->nelts > 2)
360     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
361                             _("Too many arguments given"));
362   SVN_ERR(svn_opt_parse_path(&src_peg_revision, &source,
363                              APR_ARRAY_IDX(targets, 0, const char *), pool));
364   if (targets->nelts == 2)
365     {
366       SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target,
367                                  APR_ARRAY_IDX(targets, 1, const char *),
368                                  pool));
369     }
370   else
371     {
372       target = "";
373       tgt_peg_revision.kind = svn_opt_revision_unspecified;
374     }
375
376   /* If no peg-rev was attached to the source URL, assume HEAD. */
377   /* ### But what if SOURCE is a WC path not a URL -- shouldn't we then use
378    *     BASE (but not WORKING: that would be inconsistent with 'svn merge')? */
379   if (src_peg_revision.kind == svn_opt_revision_unspecified)
380     src_peg_revision.kind = svn_opt_revision_head;
381
382   /* If no peg-rev was attached to a URL target, then assume HEAD; if
383      no peg-rev was attached to a non-URL target, then assume BASE. */
384   /* ### But we would like to be able to examine a working copy with an
385          uncommitted merge in it, so change this to use WORKING not BASE? */
386   if (tgt_peg_revision.kind == svn_opt_revision_unspecified)
387     {
388       if (svn_path_is_url(target))
389         tgt_peg_revision.kind = svn_opt_revision_head;
390       else
391         tgt_peg_revision.kind = svn_opt_revision_base;
392     }
393
394   src_start_revision = &(opt_state->start_revision);
395   if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
396     src_end_revision = src_start_revision;
397   else
398     src_end_revision = &(opt_state->end_revision);
399
400   if (!opt_state->mergeinfo_log)
401     {
402       if (opt_state->quiet)
403         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
404                                 _("--quiet (-q) option valid only with --log "
405                                   "option"));
406
407       if (opt_state->verbose)
408         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
409                                 _("--verbose (-v) option valid only with "
410                                   "--log option"));
411
412       if (opt_state->incremental)
413         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
414                                 _("--incremental option valid only with "
415                                   "--log option"));
416     }
417
418   /* Do the real work, depending on the requested data flavor. */
419   if (opt_state->show_revs == svn_cl__show_revs_merged)
420     {
421       SVN_ERR(mergeinfo_log(TRUE, target, &tgt_peg_revision,
422                             source, &src_peg_revision,
423                             src_start_revision,
424                             src_end_revision,
425                             depth, opt_state->mergeinfo_log,
426                             opt_state->quiet, opt_state->verbose,
427                             opt_state->incremental, ctx, pool));
428     }
429   else if (opt_state->show_revs == svn_cl__show_revs_eligible)
430     {
431       SVN_ERR(mergeinfo_log(FALSE, target, &tgt_peg_revision,
432                             source, &src_peg_revision,
433                             src_start_revision,
434                             src_end_revision,
435                             depth, opt_state->mergeinfo_log,
436                             opt_state->quiet, opt_state->verbose,
437                             opt_state->incremental, ctx, pool));
438     }
439   else
440     {
441       if ((opt_state->start_revision.kind != svn_opt_revision_unspecified)
442           || (opt_state->end_revision.kind != svn_opt_revision_unspecified))
443         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
444                                 _("--revision (-r) option valid only with "
445                                   "--show-revs option"));
446       if (opt_state->depth != svn_depth_unknown)
447         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
448                                 _("Depth specification options valid only "
449                                   "with --show-revs option"));
450       if (opt_state->mergeinfo_log)
451         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
452                                 _("--log option valid only with "
453                                   "--show-revs option"));
454
455
456       SVN_ERR(mergeinfo_summary(source, &src_peg_revision,
457                                 target, &tgt_peg_revision,
458                                 ctx, pool));
459     }
460   return SVN_NO_ERROR;
461 }