]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svnbench/notify.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svnbench / notify.c
1 /*
2  * notify.c:  feedback handlers for cmdline client.
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 #define APR_WANT_STDIO
31 #define APR_WANT_STRFUNC
32 #include <apr_want.h>
33
34 #include "svn_cmdline.h"
35 #include "svn_pools.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_path.h"
38 #include "svn_sorts.h"
39 #include "cl.h"
40
41 #include "svn_private_config.h"
42
43 \f
44 /* Baton for notify and friends. */
45 struct notify_baton
46 {
47   svn_boolean_t received_some_change;
48   svn_boolean_t is_checkout;
49   svn_boolean_t is_export;
50   svn_boolean_t is_wc_to_repos_copy;
51   svn_boolean_t sent_first_txdelta;
52   svn_boolean_t in_external;
53   svn_boolean_t had_print_error; /* Used to not keep printing error messages
54                                     when we've already had one print error. */
55
56   /* Conflict stats for update and merge. */
57   unsigned int text_conflicts;
58   unsigned int prop_conflicts;
59   unsigned int tree_conflicts;
60   unsigned int skipped_paths;
61   apr_hash_t *conflicted_paths;
62
63   /* The cwd, for use in decomposing absolute paths. */
64   const char *path_prefix;
65 };
66
67
68 /* Add a conflicted path to the list of conflicted paths stored
69  * in the notify baton. */
70 static void
71 add_conflicted_path(struct notify_baton *nb, const char *path)
72 {
73   apr_hash_set(nb->conflicted_paths,
74                apr_pstrdup(apr_hash_pool_get(nb->conflicted_paths), path),
75                APR_HASH_KEY_STRING, "");
76 }
77
78 /* This implements `svn_wc_notify_func2_t'.
79  * NOTE: This function can't fail, so we just ignore any print errors. */
80 static void
81 notify(void *baton, const svn_wc_notify_t *n, apr_pool_t *pool)
82 {
83   struct notify_baton *nb = baton;
84   char statchar_buf[5] = "    ";
85   const char *path_local;
86   svn_error_t *err;
87
88   if (n->url)
89     path_local = n->url;
90   else
91     {
92       if (n->path_prefix)
93         path_local = svn_cl__local_style_skip_ancestor(n->path_prefix, n->path,
94                                                        pool);
95       else /* skip nb->path_prefix, if it's non-null */
96         path_local = svn_cl__local_style_skip_ancestor(nb->path_prefix, n->path,
97                                                        pool);
98     }
99
100   switch (n->action)
101     {
102     case svn_wc_notify_skip:
103       nb->skipped_paths++;
104       if (n->content_state == svn_wc_notify_state_missing)
105         {
106           if ((err = svn_cmdline_printf
107                (pool, _("Skipped missing target: '%s'\n"),
108                 path_local)))
109             goto print_error;
110         }
111       else if (n->content_state == svn_wc_notify_state_source_missing)
112         {
113           if ((err = svn_cmdline_printf
114                (pool, _("Skipped target: '%s' -- copy-source is missing\n"),
115                 path_local)))
116             goto print_error;
117         }
118       else
119         {
120           if ((err = svn_cmdline_printf
121                (pool, _("Skipped '%s'\n"), path_local)))
122             goto print_error;
123         }
124       break;
125     case svn_wc_notify_update_skip_obstruction:
126       nb->skipped_paths++;
127       if ((err = svn_cmdline_printf(
128             pool, _("Skipped '%s' -- An obstructing working copy was found\n"),
129             path_local)))
130         goto print_error;
131       break;
132     case svn_wc_notify_update_skip_working_only:
133       nb->skipped_paths++;
134       if ((err = svn_cmdline_printf(
135             pool, _("Skipped '%s' -- Has no versioned parent\n"),
136             path_local)))
137         goto print_error;
138       break;
139     case svn_wc_notify_update_skip_access_denied:
140       nb->skipped_paths++;
141       if ((err = svn_cmdline_printf(
142             pool, _("Skipped '%s' -- Access denied\n"),
143             path_local)))
144         goto print_error;
145       break;
146     case svn_wc_notify_skip_conflicted:
147       nb->skipped_paths++;
148       if ((err = svn_cmdline_printf(
149             pool, _("Skipped '%s' -- Node remains in conflict\n"),
150             path_local)))
151         goto print_error;
152       break;
153     case svn_wc_notify_update_delete:
154     case svn_wc_notify_exclude:
155       nb->received_some_change = TRUE;
156       if ((err = svn_cmdline_printf(pool, "D    %s\n", path_local)))
157         goto print_error;
158       break;
159     case svn_wc_notify_update_broken_lock:
160       if ((err = svn_cmdline_printf(pool, "B    %s\n", path_local)))
161         goto print_error;
162       break;
163
164     case svn_wc_notify_update_external_removed:
165       nb->received_some_change = TRUE;
166       if (n->err && n->err->message)
167         {
168           if ((err = svn_cmdline_printf(pool, "Removed external '%s': %s\n",
169               path_local, n->err->message)))
170             goto print_error;
171         }
172       else
173         {
174           if ((err = svn_cmdline_printf(pool, "Removed external '%s'\n",
175                                         path_local)))
176             goto print_error;
177         }
178       break;
179
180     case svn_wc_notify_left_local_modifications:
181       if ((err = svn_cmdline_printf(pool, "Left local modifications as '%s'\n",
182                                         path_local)))
183         goto print_error;
184       break;
185
186     case svn_wc_notify_update_replace:
187       nb->received_some_change = TRUE;
188       if ((err = svn_cmdline_printf(pool, "R    %s\n", path_local)))
189         goto print_error;
190       break;
191
192     case svn_wc_notify_update_add:
193       nb->received_some_change = TRUE;
194       if (n->content_state == svn_wc_notify_state_conflicted)
195         {
196           nb->text_conflicts++;
197           add_conflicted_path(nb, n->path);
198           if ((err = svn_cmdline_printf(pool, "C    %s\n", path_local)))
199             goto print_error;
200         }
201       else
202         {
203           if ((err = svn_cmdline_printf(pool, "A    %s\n", path_local)))
204             goto print_error;
205         }
206       break;
207
208     case svn_wc_notify_exists:
209       nb->received_some_change = TRUE;
210       if (n->content_state == svn_wc_notify_state_conflicted)
211         {
212           nb->text_conflicts++;
213           add_conflicted_path(nb, n->path);
214           statchar_buf[0] = 'C';
215         }
216       else
217         statchar_buf[0] = 'E';
218
219       if (n->prop_state == svn_wc_notify_state_conflicted)
220         {
221           nb->prop_conflicts++;
222           add_conflicted_path(nb, n->path);
223           statchar_buf[1] = 'C';
224         }
225       else if (n->prop_state == svn_wc_notify_state_merged)
226         statchar_buf[1] = 'G';
227
228       if ((err = svn_cmdline_printf(pool, "%s %s\n", statchar_buf, path_local)))
229         goto print_error;
230       break;
231
232     case svn_wc_notify_restore:
233       if ((err = svn_cmdline_printf(pool, _("Restored '%s'\n"),
234                                     path_local)))
235         goto print_error;
236       break;
237
238     case svn_wc_notify_revert:
239       if ((err = svn_cmdline_printf(pool, _("Reverted '%s'\n"),
240                                     path_local)))
241         goto print_error;
242       break;
243
244     case svn_wc_notify_failed_revert:
245       if (( err = svn_cmdline_printf(pool, _("Failed to revert '%s' -- "
246                                              "try updating instead.\n"),
247                                      path_local)))
248         goto print_error;
249       break;
250
251     case svn_wc_notify_resolved:
252       if ((err = svn_cmdline_printf(pool,
253                                     _("Resolved conflicted state of '%s'\n"),
254                                     path_local)))
255         goto print_error;
256       break;
257
258     case svn_wc_notify_add:
259       /* We *should* only get the MIME_TYPE if PATH is a file.  If we
260          do get it, and the mime-type is not textual, note that this
261          is a binary addition. */
262       if (n->mime_type && (svn_mime_type_is_binary(n->mime_type)))
263         {
264           if ((err = svn_cmdline_printf(pool, "A  (bin)  %s\n",
265                                         path_local)))
266             goto print_error;
267         }
268       else
269         {
270           if ((err = svn_cmdline_printf(pool, "A         %s\n",
271                                         path_local)))
272             goto print_error;
273         }
274       break;
275
276     case svn_wc_notify_delete:
277       nb->received_some_change = TRUE;
278       if ((err = svn_cmdline_printf(pool, "D         %s\n",
279                                     path_local)))
280         goto print_error;
281       break;
282
283     case svn_wc_notify_patch:
284       {
285         nb->received_some_change = TRUE;
286         if (n->content_state == svn_wc_notify_state_conflicted)
287           {
288             nb->text_conflicts++;
289             add_conflicted_path(nb, n->path);
290             statchar_buf[0] = 'C';
291           }
292         else if (n->kind == svn_node_file)
293           {
294             if (n->content_state == svn_wc_notify_state_merged)
295               statchar_buf[0] = 'G';
296             else if (n->content_state == svn_wc_notify_state_changed)
297               statchar_buf[0] = 'U';
298           }
299
300         if (n->prop_state == svn_wc_notify_state_conflicted)
301           {
302             nb->prop_conflicts++;
303             add_conflicted_path(nb, n->path);
304             statchar_buf[1] = 'C';
305           }
306         else if (n->prop_state == svn_wc_notify_state_changed)
307               statchar_buf[1] = 'U';
308
309         if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
310           {
311             if ((err = svn_cmdline_printf(pool, "%s      %s\n",
312                                           statchar_buf, path_local)))
313               goto print_error;
314           }
315       }
316       break;
317
318     case svn_wc_notify_patch_applied_hunk:
319       nb->received_some_change = TRUE;
320       if (n->hunk_original_start != n->hunk_matched_line)
321         {
322           apr_uint64_t off;
323           const char *s;
324           const char *minus;
325
326           if (n->hunk_matched_line > n->hunk_original_start)
327             {
328               off = n->hunk_matched_line - n->hunk_original_start;
329               minus = "";
330             }
331           else
332             {
333               off = n->hunk_original_start - n->hunk_matched_line;
334               minus = "-";
335             }
336
337           /* ### We're creating the localized strings without
338            * ### APR_INT64_T_FMT since it isn't translator-friendly */
339           if (n->hunk_fuzz)
340             {
341
342               if (n->prop_name)
343                 {
344                   s = _(">         applied hunk ## -%lu,%lu +%lu,%lu ## "
345                         "with offset %s");
346
347                   err = svn_cmdline_printf(pool,
348                                            apr_pstrcat(pool, s,
349                                                        "%"APR_UINT64_T_FMT
350                                                        " and fuzz %lu (%s)\n",
351                                                        SVN_VA_NULL),
352                                            n->hunk_original_start,
353                                            n->hunk_original_length,
354                                            n->hunk_modified_start,
355                                            n->hunk_modified_length,
356                                            minus, off, n->hunk_fuzz,
357                                            n->prop_name);
358                 }
359               else
360                 {
361                   s = _(">         applied hunk @@ -%lu,%lu +%lu,%lu @@ "
362                         "with offset %s");
363
364                   err = svn_cmdline_printf(pool,
365                                            apr_pstrcat(pool, s,
366                                                        "%"APR_UINT64_T_FMT
367                                                        " and fuzz %lu\n",
368                                                        SVN_VA_NULL),
369                                            n->hunk_original_start,
370                                            n->hunk_original_length,
371                                            n->hunk_modified_start,
372                                            n->hunk_modified_length,
373                                            minus, off, n->hunk_fuzz);
374                 }
375
376               if (err)
377                 goto print_error;
378             }
379           else
380             {
381
382               if (n->prop_name)
383                 {
384                   s = _(">         applied hunk ## -%lu,%lu +%lu,%lu ## "
385                         "with offset %s");
386                   err = svn_cmdline_printf(pool,
387                                             apr_pstrcat(pool, s,
388                                                         "%"APR_UINT64_T_FMT" (%s)\n",
389                                                         SVN_VA_NULL),
390                                             n->hunk_original_start,
391                                             n->hunk_original_length,
392                                             n->hunk_modified_start,
393                                             n->hunk_modified_length,
394                                             minus, off, n->prop_name);
395                 }
396               else
397                 {
398                   s = _(">         applied hunk @@ -%lu,%lu +%lu,%lu @@ "
399                         "with offset %s");
400                   err = svn_cmdline_printf(pool,
401                                            apr_pstrcat(pool, s,
402                                                        "%"APR_UINT64_T_FMT"\n",
403                                                        SVN_VA_NULL),
404                                            n->hunk_original_start,
405                                            n->hunk_original_length,
406                                            n->hunk_modified_start,
407                                            n->hunk_modified_length,
408                                            minus, off);
409                 }
410
411               if (err)
412                 goto print_error;
413             }
414         }
415       else if (n->hunk_fuzz)
416         {
417           if (n->prop_name)
418             err = svn_cmdline_printf(pool,
419                           _(">         applied hunk ## -%lu,%lu +%lu,%lu ## "
420                                         "with fuzz %lu (%s)\n"),
421                                         n->hunk_original_start,
422                                         n->hunk_original_length,
423                                         n->hunk_modified_start,
424                                         n->hunk_modified_length,
425                                         n->hunk_fuzz,
426                                         n->prop_name);
427           else
428             err = svn_cmdline_printf(pool,
429                           _(">         applied hunk @@ -%lu,%lu +%lu,%lu @@ "
430                                         "with fuzz %lu\n"),
431                                         n->hunk_original_start,
432                                         n->hunk_original_length,
433                                         n->hunk_modified_start,
434                                         n->hunk_modified_length,
435                                         n->hunk_fuzz);
436           if (err)
437             goto print_error;
438
439         }
440       break;
441
442     case svn_wc_notify_patch_rejected_hunk:
443       nb->received_some_change = TRUE;
444
445       if (n->prop_name)
446         err = svn_cmdline_printf(pool,
447                                  _(">         rejected hunk "
448                                    "## -%lu,%lu +%lu,%lu ## (%s)\n"),
449                                  n->hunk_original_start,
450                                  n->hunk_original_length,
451                                  n->hunk_modified_start,
452                                  n->hunk_modified_length,
453                                  n->prop_name);
454       else
455         err = svn_cmdline_printf(pool,
456                                  _(">         rejected hunk "
457                                    "@@ -%lu,%lu +%lu,%lu @@\n"),
458                                  n->hunk_original_start,
459                                  n->hunk_original_length,
460                                  n->hunk_modified_start,
461                                  n->hunk_modified_length);
462       if (err)
463         goto print_error;
464       break;
465
466     case svn_wc_notify_patch_hunk_already_applied:
467       nb->received_some_change = TRUE;
468       if (n->prop_name)
469         err = svn_cmdline_printf(pool,
470                                  _(">         hunk "
471                                    "## -%lu,%lu +%lu,%lu ## "
472                                    "already applied (%s)\n"),
473                                  n->hunk_original_start,
474                                  n->hunk_original_length,
475                                  n->hunk_modified_start,
476                                  n->hunk_modified_length,
477                                  n->prop_name);
478       else
479         err = svn_cmdline_printf(pool,
480                                  _(">         hunk "
481                                    "@@ -%lu,%lu +%lu,%lu @@ "
482                                    "already applied\n"),
483                                  n->hunk_original_start,
484                                  n->hunk_original_length,
485                                  n->hunk_modified_start,
486                                  n->hunk_modified_length);
487       if (err)
488         goto print_error;
489       break;
490
491     case svn_wc_notify_update_update:
492     case svn_wc_notify_merge_record_info:
493       {
494         if (n->content_state == svn_wc_notify_state_conflicted)
495           {
496             nb->text_conflicts++;
497             add_conflicted_path(nb, n->path);
498             statchar_buf[0] = 'C';
499           }
500         else if (n->kind == svn_node_file)
501           {
502             if (n->content_state == svn_wc_notify_state_merged)
503               statchar_buf[0] = 'G';
504             else if (n->content_state == svn_wc_notify_state_changed)
505               statchar_buf[0] = 'U';
506           }
507
508         if (n->prop_state == svn_wc_notify_state_conflicted)
509           {
510             nb->prop_conflicts++;
511             add_conflicted_path(nb, n->path);
512             statchar_buf[1] = 'C';
513           }
514         else if (n->prop_state == svn_wc_notify_state_merged)
515           statchar_buf[1] = 'G';
516         else if (n->prop_state == svn_wc_notify_state_changed)
517           statchar_buf[1] = 'U';
518
519         if (n->lock_state == svn_wc_notify_lock_state_unlocked)
520           statchar_buf[2] = 'B';
521
522         if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
523           nb->received_some_change = TRUE;
524
525         if (statchar_buf[0] != ' ' || statchar_buf[1] != ' '
526             || statchar_buf[2] != ' ')
527           {
528             if ((err = svn_cmdline_printf(pool, "%s %s\n",
529                                           statchar_buf, path_local)))
530               goto print_error;
531           }
532       }
533       break;
534
535     case svn_wc_notify_update_external:
536       /* Remember that we're now "inside" an externals definition. */
537       nb->in_external = TRUE;
538
539       /* Currently this is used for checkouts and switches too.  If we
540          want different output, we'll have to add new actions. */
541       if ((err = svn_cmdline_printf(pool,
542                                     _("\nFetching external item into '%s':\n"),
543                                     path_local)))
544         goto print_error;
545       break;
546
547     case svn_wc_notify_failed_external:
548       /* If we are currently inside the handling of an externals
549          definition, then we can simply present n->err as a warning
550          and feel confident that after this, we aren't handling that
551          externals definition any longer. */
552       if (nb->in_external)
553         {
554           svn_handle_warning2(stderr, n->err, "svn: ");
555           nb->in_external = FALSE;
556           if ((err = svn_cmdline_printf(pool, "\n")))
557             goto print_error;
558         }
559       /* Otherwise, we'll just print two warnings.  Why?  Because
560          svn_handle_warning2() only shows the single "best message",
561          but we have two pretty important ones: that the external at
562          '/some/path' didn't pan out, and then the more specific
563          reason why (from n->err). */
564       else
565         {
566           svn_error_t *warn_err =
567             svn_error_createf(SVN_ERR_BASE, NULL,
568                               _("Error handling externals definition for '%s':"),
569                               path_local);
570           svn_handle_warning2(stderr, warn_err, "svn: ");
571           svn_error_clear(warn_err);
572           svn_handle_warning2(stderr, n->err, "svn: ");
573         }
574       break;
575
576     case svn_wc_notify_update_started:
577       if (! (nb->in_external ||
578              nb->is_checkout ||
579              nb->is_export))
580         {
581           if ((err = svn_cmdline_printf(pool, _("Updating '%s':\n"),
582                                         path_local)))
583             goto print_error;
584         }
585       break;
586
587     case svn_wc_notify_update_completed:
588       {
589         if (SVN_IS_VALID_REVNUM(n->revision))
590           {
591             if (nb->is_export)
592               {
593                 if ((err = svn_cmdline_printf
594                      (pool, nb->in_external
595                       ? _("Exported external at revision %ld.\n")
596                       : _("Exported revision %ld.\n"),
597                       n->revision)))
598                   goto print_error;
599               }
600             else if (nb->is_checkout)
601               {
602                 if ((err = svn_cmdline_printf
603                      (pool, nb->in_external
604                       ? _("Checked out external at revision %ld.\n")
605                       : _("Checked out revision %ld.\n"),
606                       n->revision)))
607                   goto print_error;
608               }
609             else
610               {
611                 if (nb->received_some_change)
612                   {
613                     nb->received_some_change = FALSE;
614                     if ((err = svn_cmdline_printf
615                          (pool, nb->in_external
616                           ? _("Updated external to revision %ld.\n")
617                           : _("Updated to revision %ld.\n"),
618                           n->revision)))
619                       goto print_error;
620                   }
621                 else
622                   {
623                     if ((err = svn_cmdline_printf
624                          (pool, nb->in_external
625                           ? _("External at revision %ld.\n")
626                           : _("At revision %ld.\n"),
627                           n->revision)))
628                       goto print_error;
629                   }
630               }
631           }
632         else  /* no revision */
633           {
634             if (nb->is_export)
635               {
636                 if ((err = svn_cmdline_printf
637                      (pool, nb->in_external
638                       ? _("External export complete.\n")
639                       : _("Export complete.\n"))))
640                   goto print_error;
641               }
642             else if (nb->is_checkout)
643               {
644                 if ((err = svn_cmdline_printf
645                      (pool, nb->in_external
646                       ? _("External checkout complete.\n")
647                       : _("Checkout complete.\n"))))
648                   goto print_error;
649               }
650             else
651               {
652                 if ((err = svn_cmdline_printf
653                      (pool, nb->in_external
654                       ? _("External update complete.\n")
655                       : _("Update complete.\n"))))
656                   goto print_error;
657               }
658           }
659       }
660
661       if (nb->in_external)
662         {
663           nb->in_external = FALSE;
664           if ((err = svn_cmdline_printf(pool, "\n")))
665             goto print_error;
666         }
667       break;
668
669     case svn_wc_notify_status_external:
670       if ((err = svn_cmdline_printf
671            (pool, _("\nPerforming status on external item at '%s':\n"),
672             path_local)))
673         goto print_error;
674       break;
675
676     case svn_wc_notify_status_completed:
677       if (SVN_IS_VALID_REVNUM(n->revision))
678         if ((err = svn_cmdline_printf(pool,
679                                       _("Status against revision: %6ld\n"),
680                                       n->revision)))
681           goto print_error;
682       break;
683
684     case svn_wc_notify_commit_modified:
685       /* xgettext: Align the %s's on this and the following 4 messages */
686       if ((err = svn_cmdline_printf(pool,
687                                     nb->is_wc_to_repos_copy
688                                       ? _("Sending copy of       %s\n")
689                                       : _("Sending        %s\n"),
690                                     path_local)))
691         goto print_error;
692       break;
693
694     case svn_wc_notify_commit_added:
695     case svn_wc_notify_commit_copied:
696       if (n->mime_type && svn_mime_type_is_binary(n->mime_type))
697         {
698           if ((err = svn_cmdline_printf(pool,
699                                         nb->is_wc_to_repos_copy
700                                           ? _("Adding copy of (bin)  %s\n")
701                                           : _("Adding  (bin)  %s\n"),
702                                         path_local)))
703           goto print_error;
704         }
705       else
706         {
707           if ((err = svn_cmdline_printf(pool,
708                                         nb->is_wc_to_repos_copy
709                                           ? _("Adding copy of        %s\n")
710                                           : _("Adding         %s\n"),
711                                         path_local)))
712             goto print_error;
713         }
714       break;
715
716     case svn_wc_notify_commit_deleted:
717       if ((err = svn_cmdline_printf(pool,
718                                     nb->is_wc_to_repos_copy
719                                       ? _("Deleting copy of      %s\n")
720                                       : _("Deleting       %s\n"),
721                                     path_local)))
722         goto print_error;
723       break;
724
725     case svn_wc_notify_commit_replaced:
726     case svn_wc_notify_commit_copied_replaced:
727       if ((err = svn_cmdline_printf(pool,
728                                     nb->is_wc_to_repos_copy
729                                       ? _("Replacing copy of     %s\n")
730                                       : _("Replacing      %s\n"),
731                                     path_local)))
732         goto print_error;
733       break;
734
735     case svn_wc_notify_commit_postfix_txdelta:
736       if (! nb->sent_first_txdelta)
737         {
738           nb->sent_first_txdelta = TRUE;
739           if ((err = svn_cmdline_printf(pool,
740                                         _("Transmitting file data "))))
741             goto print_error;
742         }
743
744       if ((err = svn_cmdline_printf(pool, ".")))
745         goto print_error;
746       break;
747
748     case svn_wc_notify_locked:
749       if ((err = svn_cmdline_printf(pool, _("'%s' locked by user '%s'.\n"),
750                                     path_local, n->lock->owner)))
751         goto print_error;
752       break;
753
754     case svn_wc_notify_unlocked:
755       if ((err = svn_cmdline_printf(pool, _("'%s' unlocked.\n"),
756                                     path_local)))
757         goto print_error;
758       break;
759
760     case svn_wc_notify_failed_lock:
761     case svn_wc_notify_failed_unlock:
762       svn_handle_warning2(stderr, n->err, "svn: ");
763       break;
764
765     case svn_wc_notify_changelist_set:
766       if ((err = svn_cmdline_printf(pool, "A [%s] %s\n",
767                                     n->changelist_name, path_local)))
768         goto print_error;
769       break;
770
771     case svn_wc_notify_changelist_clear:
772     case svn_wc_notify_changelist_moved:
773       if ((err = svn_cmdline_printf(pool,
774                                     "D [%s] %s\n",
775                                     n->changelist_name, path_local)))
776         goto print_error;
777       break;
778
779     case svn_wc_notify_merge_begin:
780       if (n->merge_range == NULL)
781         err = svn_cmdline_printf(pool,
782                                  _("--- Merging differences between "
783                                    "repository URLs into '%s':\n"),
784                                  path_local);
785       else if (n->merge_range->start == n->merge_range->end - 1
786           || n->merge_range->start == n->merge_range->end)
787         err = svn_cmdline_printf(pool, _("--- Merging r%ld into '%s':\n"),
788                                  n->merge_range->end, path_local);
789       else if (n->merge_range->start - 1 == n->merge_range->end)
790         err = svn_cmdline_printf(pool,
791                                  _("--- Reverse-merging r%ld into '%s':\n"),
792                                  n->merge_range->start, path_local);
793       else if (n->merge_range->start < n->merge_range->end)
794         err = svn_cmdline_printf(pool,
795                                  _("--- Merging r%ld through r%ld into "
796                                    "'%s':\n"),
797                                  n->merge_range->start + 1,
798                                  n->merge_range->end, path_local);
799       else /* n->merge_range->start > n->merge_range->end - 1 */
800         err = svn_cmdline_printf(pool,
801                                  _("--- Reverse-merging r%ld through r%ld "
802                                    "into '%s':\n"),
803                                  n->merge_range->start,
804                                  n->merge_range->end + 1, path_local);
805       if (err)
806         goto print_error;
807       break;
808
809     case svn_wc_notify_merge_record_info_begin:
810       if (!n->merge_range)
811         {
812           err = svn_cmdline_printf(pool,
813                                    _("--- Recording mergeinfo for merge "
814                                      "between repository URLs into '%s':\n"),
815                                    path_local);
816         }
817       else
818         {
819           if (n->merge_range->start == n->merge_range->end - 1
820               || n->merge_range->start == n->merge_range->end)
821             err = svn_cmdline_printf(
822               pool,
823               _("--- Recording mergeinfo for merge of r%ld into '%s':\n"),
824               n->merge_range->end, path_local);
825           else if (n->merge_range->start - 1 == n->merge_range->end)
826             err = svn_cmdline_printf(
827               pool,
828               _("--- Recording mergeinfo for reverse merge of r%ld into '%s':\n"),
829               n->merge_range->start, path_local);
830            else if (n->merge_range->start < n->merge_range->end)
831              err = svn_cmdline_printf(
832                pool,
833                _("--- Recording mergeinfo for merge of r%ld through r%ld into '%s':\n"),
834                n->merge_range->start + 1, n->merge_range->end, path_local);
835            else /* n->merge_range->start > n->merge_range->end - 1 */
836              err = svn_cmdline_printf(
837                pool,
838                _("--- Recording mergeinfo for reverse merge of r%ld through r%ld into '%s':\n"),
839                n->merge_range->start, n->merge_range->end + 1, path_local);
840         }
841
842       if (err)
843         goto print_error;
844       break;
845
846     case svn_wc_notify_merge_elide_info:
847       if ((err = svn_cmdline_printf(pool,
848                                     _("--- Eliding mergeinfo from '%s':\n"),
849                                     path_local)))
850         goto print_error;
851       break;
852
853     case svn_wc_notify_foreign_merge_begin:
854       if (n->merge_range == NULL)
855         err = svn_cmdline_printf(pool,
856                                  _("--- Merging differences between "
857                                    "foreign repository URLs into '%s':\n"),
858                                  path_local);
859       else if (n->merge_range->start == n->merge_range->end - 1
860           || n->merge_range->start == n->merge_range->end)
861         err = svn_cmdline_printf(pool,
862                                  _("--- Merging (from foreign repository) "
863                                    "r%ld into '%s':\n"),
864                                  n->merge_range->end, path_local);
865       else if (n->merge_range->start - 1 == n->merge_range->end)
866         err = svn_cmdline_printf(pool,
867                                  _("--- Reverse-merging (from foreign "
868                                    "repository) r%ld into '%s':\n"),
869                                  n->merge_range->start, path_local);
870       else if (n->merge_range->start < n->merge_range->end)
871         err = svn_cmdline_printf(pool,
872                                  _("--- Merging (from foreign repository) "
873                                    "r%ld through r%ld into '%s':\n"),
874                                  n->merge_range->start + 1,
875                                  n->merge_range->end, path_local);
876       else /* n->merge_range->start > n->merge_range->end - 1 */
877         err = svn_cmdline_printf(pool,
878                                  _("--- Reverse-merging (from foreign "
879                                    "repository) r%ld through r%ld into "
880                                    "'%s':\n"),
881                                  n->merge_range->start,
882                                  n->merge_range->end + 1, path_local);
883       if (err)
884         goto print_error;
885       break;
886
887     case svn_wc_notify_tree_conflict:
888       nb->tree_conflicts++;
889       add_conflicted_path(nb, n->path);
890       if ((err = svn_cmdline_printf(pool, "   C %s\n", path_local)))
891         goto print_error;
892       break;
893
894     case svn_wc_notify_update_shadowed_add:
895       nb->received_some_change = TRUE;
896       if ((err = svn_cmdline_printf(pool, "   A %s\n", path_local)))
897         goto print_error;
898       break;
899
900     case svn_wc_notify_update_shadowed_update:
901       nb->received_some_change = TRUE;
902       if ((err = svn_cmdline_printf(pool, "   U %s\n", path_local)))
903         goto print_error;
904       break;
905
906     case svn_wc_notify_update_shadowed_delete:
907       nb->received_some_change = TRUE;
908       if ((err = svn_cmdline_printf(pool, "   D %s\n", path_local)))
909         goto print_error;
910       break;
911
912     case svn_wc_notify_property_modified:
913     case svn_wc_notify_property_added:
914         err = svn_cmdline_printf(pool,
915                                  _("property '%s' set on '%s'\n"),
916                                  n->prop_name, path_local);
917         if (err)
918           goto print_error;
919       break;
920
921     case svn_wc_notify_property_deleted:
922         err = svn_cmdline_printf(pool,
923                                  _("property '%s' deleted from '%s'.\n"),
924                                  n->prop_name, path_local);
925         if (err)
926           goto print_error;
927       break;
928
929     case svn_wc_notify_property_deleted_nonexistent:
930         err = svn_cmdline_printf(pool,
931                                  _("Attempting to delete nonexistent "
932                                    "property '%s' on '%s'\n"), n->prop_name,
933                                    path_local);
934         if (err)
935           goto print_error;
936       break;
937
938     case svn_wc_notify_revprop_set:
939         err = svn_cmdline_printf(pool,
940                           _("property '%s' set on repository revision %ld\n"),
941                           n->prop_name, n->revision);
942         if (err)
943           goto print_error;
944       break;
945
946     case svn_wc_notify_revprop_deleted:
947         err = svn_cmdline_printf(pool,
948                      _("property '%s' deleted from repository revision %ld\n"),
949                      n->prop_name, n->revision);
950         if (err)
951           goto print_error;
952       break;
953
954     case svn_wc_notify_upgraded_path:
955         err = svn_cmdline_printf(pool, _("Upgraded '%s'\n"), path_local);
956         if (err)
957           goto print_error;
958       break;
959
960     case svn_wc_notify_url_redirect:
961       err = svn_cmdline_printf(pool, _("Redirecting to URL '%s':\n"),
962                                n->url);
963       if (err)
964         goto print_error;
965       break;
966
967     case svn_wc_notify_path_nonexistent:
968       err = svn_cmdline_printf(pool, _("'%s' is not under version control"),
969                                path_local);
970       if (err)
971         goto print_error;
972       break;
973
974     case svn_wc_notify_conflict_resolver_starting:
975       /* Once all operations invoke the interactive conflict resolution after
976        * they've completed, we can run svn_cl__print_conflict_stats() here. */
977       break;
978
979     case svn_wc_notify_conflict_resolver_done:
980       break;
981
982     default:
983       break;
984     }
985
986   if ((err = svn_cmdline_fflush(stdout)))
987     goto print_error;
988
989   return;
990
991  print_error:
992   /* If we had no errors before, print this error to stderr. Else, don't print
993      anything.  The user already knows there were some output errors,
994      so there is no point in flooding her with an error per notification. */
995   if (!nb->had_print_error)
996     {
997       nb->had_print_error = TRUE;
998       /* Issue #3014:
999        * Don't print anything on broken pipes. The pipe was likely
1000        * closed by the process at the other end. We expect that
1001        * process to perform error reporting as necessary.
1002        *
1003        * ### This assumes that there is only one error in a chain for
1004        * ### SVN_ERR_IO_PIPE_WRITE_ERROR. See svn_cmdline_fputs(). */
1005       if (err->apr_err != SVN_ERR_IO_PIPE_WRITE_ERROR)
1006         svn_handle_error2(err, stderr, FALSE, "svn: ");
1007     }
1008   svn_error_clear(err);
1009 }
1010
1011
1012 svn_error_t *
1013 svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
1014                      void **notify_baton_p,
1015                      apr_pool_t *pool)
1016 {
1017   struct notify_baton *nb = apr_pcalloc(pool, sizeof(*nb));
1018
1019   nb->received_some_change = FALSE;
1020   nb->sent_first_txdelta = FALSE;
1021   nb->is_checkout = FALSE;
1022   nb->is_export = FALSE;
1023   nb->is_wc_to_repos_copy = FALSE;
1024   nb->in_external = FALSE;
1025   nb->had_print_error = FALSE;
1026   nb->text_conflicts = 0;
1027   nb->prop_conflicts = 0;
1028   nb->tree_conflicts = 0;
1029   nb->skipped_paths = 0;
1030   nb->conflicted_paths = apr_hash_make(pool);
1031   SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));
1032
1033   *notify_func_p = notify;
1034   *notify_baton_p = nb;
1035   return SVN_NO_ERROR;
1036 }
1037
1038 svn_error_t *
1039 svn_cl__notifier_mark_export(void *baton)
1040 {
1041   struct notify_baton *nb = baton;
1042
1043   nb->is_export = TRUE;
1044   return SVN_NO_ERROR;
1045 }