1 /*
2  * debug_editor.c :  An editor that writes the operations it does to stderr.
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 #include "svn_io.h"
25 
26 #include "private/svn_delta_private.h"
27 
28 struct edit_baton
29 {
30   const svn_delta_editor_t *wrapped_editor;
31   void *wrapped_edit_baton;
32 
33   int indent_level;
34 
35   svn_stream_t *out;
36   const char *prefix;
37 };
38 
39 struct dir_baton
40 {
41   void *edit_baton;
42   void *wrapped_dir_baton;
43 };
44 
45 struct file_baton
46 {
47   void *edit_baton;
48   void *wrapped_file_baton;
49 };
50 
51 static svn_error_t *
write_indent(struct edit_baton * eb,apr_pool_t * pool)52 write_indent(struct edit_baton *eb, apr_pool_t *pool)
53 {
54   int i;
55 
56   SVN_ERR(svn_stream_puts(eb->out, eb->prefix));
57   for (i = 0; i < eb->indent_level; ++i)
58     SVN_ERR(svn_stream_puts(eb->out, " "));
59 
60   return SVN_NO_ERROR;
61 }
62 
63 static svn_error_t *
set_target_revision(void * edit_baton,svn_revnum_t target_revision,apr_pool_t * pool)64 set_target_revision(void *edit_baton,
65                     svn_revnum_t target_revision,
66                     apr_pool_t *pool)
67 {
68   struct edit_baton *eb = edit_baton;
69 
70   SVN_ERR(write_indent(eb, pool));
71   SVN_ERR(svn_stream_printf(eb->out, pool, "set_target_revision : %ld\n",
72                             target_revision));
73 
74   if (eb->wrapped_editor)
75     SVN_ERR(eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton,
76                                                     target_revision,
77                                                     pool));
78   return SVN_NO_ERROR;
79 }
80 
81 static svn_error_t *
open_root(void * edit_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** root_baton)82 open_root(void *edit_baton,
83           svn_revnum_t base_revision,
84           apr_pool_t *pool,
85           void **root_baton)
86 {
87   struct edit_baton *eb = edit_baton;
88   struct dir_baton *dir_baton = apr_palloc(pool, sizeof(*dir_baton));
89 
90   SVN_ERR(write_indent(eb, pool));
91   SVN_ERR(svn_stream_printf(eb->out, pool, "open_root : %ld\n",
92                             base_revision));
93   eb->indent_level++;
94 
95   if (eb->wrapped_editor)
96     SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton,
97                                           base_revision,
98                                           pool,
99                                           &dir_baton->wrapped_dir_baton));
100 
101   dir_baton->edit_baton = edit_baton;
102 
103   *root_baton = dir_baton;
104 
105   return SVN_NO_ERROR;
106 }
107 
108 static svn_error_t *
delete_entry(const char * path,svn_revnum_t base_revision,void * parent_baton,apr_pool_t * pool)109 delete_entry(const char *path,
110              svn_revnum_t base_revision,
111              void *parent_baton,
112              apr_pool_t *pool)
113 {
114   struct dir_baton *pb = parent_baton;
115   struct edit_baton *eb = pb->edit_baton;
116 
117   SVN_ERR(write_indent(eb, pool));
118   SVN_ERR(svn_stream_printf(eb->out, pool, "delete_entry : %s:%ld\n",
119                             path, base_revision));
120 
121   if (eb->wrapped_editor)
122     SVN_ERR(eb->wrapped_editor->delete_entry(path,
123                                              base_revision,
124                                              pb->wrapped_dir_baton,
125                                              pool));
126   return SVN_NO_ERROR;
127 }
128 
129 static svn_error_t *
add_directory(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_revision,apr_pool_t * pool,void ** child_baton)130 add_directory(const char *path,
131               void *parent_baton,
132               const char *copyfrom_path,
133               svn_revnum_t copyfrom_revision,
134               apr_pool_t *pool,
135               void **child_baton)
136 {
137   struct dir_baton *pb = parent_baton;
138   struct edit_baton *eb = pb->edit_baton;
139   struct dir_baton *b = apr_palloc(pool, sizeof(*b));
140 
141   SVN_ERR(write_indent(eb, pool));
142   SVN_ERR(svn_stream_printf(eb->out, pool,
143                             "add_directory : '%s' [from '%s':%ld]\n",
144                             path, copyfrom_path, copyfrom_revision));
145   eb->indent_level++;
146 
147   if (eb->wrapped_editor)
148     SVN_ERR(eb->wrapped_editor->add_directory(path,
149                                               pb->wrapped_dir_baton,
150                                               copyfrom_path,
151                                               copyfrom_revision,
152                                               pool,
153                                               &b->wrapped_dir_baton));
154 
155   b->edit_baton = eb;
156   *child_baton = b;
157 
158   return SVN_NO_ERROR;
159 }
160 
161 static svn_error_t *
open_directory(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** child_baton)162 open_directory(const char *path,
163                void *parent_baton,
164                svn_revnum_t base_revision,
165                apr_pool_t *pool,
166                void **child_baton)
167 {
168   struct dir_baton *pb = parent_baton;
169   struct edit_baton *eb = pb->edit_baton;
170   struct dir_baton *db = apr_palloc(pool, sizeof(*db));
171 
172   SVN_ERR(write_indent(eb, pool));
173   SVN_ERR(svn_stream_printf(eb->out, pool, "open_directory : '%s':%ld\n",
174                             path, base_revision));
175   eb->indent_level++;
176 
177   if (eb->wrapped_editor)
178     SVN_ERR(eb->wrapped_editor->open_directory(path,
179                                                pb->wrapped_dir_baton,
180                                                base_revision,
181                                                pool,
182                                                &db->wrapped_dir_baton));
183 
184   db->edit_baton = eb;
185   *child_baton = db;
186 
187   return SVN_NO_ERROR;
188 }
189 
190 static svn_error_t *
add_file(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_revision,apr_pool_t * pool,void ** file_baton)191 add_file(const char *path,
192          void *parent_baton,
193          const char *copyfrom_path,
194          svn_revnum_t copyfrom_revision,
195          apr_pool_t *pool,
196          void **file_baton)
197 {
198   struct dir_baton *pb = parent_baton;
199   struct edit_baton *eb = pb->edit_baton;
200   struct file_baton *fb = apr_palloc(pool, sizeof(*fb));
201 
202   SVN_ERR(write_indent(eb, pool));
203   SVN_ERR(svn_stream_printf(eb->out, pool,
204                             "add_file : '%s' [from '%s':%ld]\n",
205                             path, copyfrom_path, copyfrom_revision));
206 
207   eb->indent_level++;
208 
209   if (eb->wrapped_editor)
210     SVN_ERR(eb->wrapped_editor->add_file(path,
211                                          pb->wrapped_dir_baton,
212                                          copyfrom_path,
213                                          copyfrom_revision,
214                                          pool,
215                                          &fb->wrapped_file_baton));
216 
217   fb->edit_baton = eb;
218   *file_baton = fb;
219 
220   return SVN_NO_ERROR;
221 }
222 
223 static svn_error_t *
open_file(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** file_baton)224 open_file(const char *path,
225           void *parent_baton,
226           svn_revnum_t base_revision,
227           apr_pool_t *pool,
228           void **file_baton)
229 {
230   struct dir_baton *pb = parent_baton;
231   struct edit_baton *eb = pb->edit_baton;
232   struct file_baton *fb = apr_palloc(pool, sizeof(*fb));
233 
234   SVN_ERR(write_indent(eb, pool));
235   SVN_ERR(svn_stream_printf(eb->out, pool, "open_file : '%s':%ld\n",
236                             path, base_revision));
237 
238   eb->indent_level++;
239 
240   if (eb->wrapped_editor)
241     SVN_ERR(eb->wrapped_editor->open_file(path,
242                                           pb->wrapped_dir_baton,
243                                           base_revision,
244                                           pool,
245                                           &fb->wrapped_file_baton));
246 
247   fb->edit_baton = eb;
248   *file_baton = fb;
249 
250   return SVN_NO_ERROR;
251 }
252 
253 static svn_error_t *
apply_textdelta(void * file_baton,const char * base_checksum,apr_pool_t * pool,svn_txdelta_window_handler_t * handler,void ** handler_baton)254 apply_textdelta(void *file_baton,
255                 const char *base_checksum,
256                 apr_pool_t *pool,
257                 svn_txdelta_window_handler_t *handler,
258                 void **handler_baton)
259 {
260   struct file_baton *fb = file_baton;
261   struct edit_baton *eb = fb->edit_baton;
262 
263   SVN_ERR(write_indent(eb, pool));
264   SVN_ERR(svn_stream_printf(eb->out, pool, "apply_textdelta : %s\n",
265                             base_checksum));
266 
267   if (eb->wrapped_editor)
268     SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_file_baton,
269                                                 base_checksum,
270                                                 pool,
271                                                 handler,
272                                                 handler_baton));
273 
274   return SVN_NO_ERROR;
275 }
276 
277 static svn_error_t *
apply_textdelta_stream(const struct svn_delta_editor_t * editor,void * file_baton,const char * base_checksum,svn_txdelta_stream_open_func_t open_func,void * open_baton,apr_pool_t * scratch_pool)278 apply_textdelta_stream(const struct svn_delta_editor_t *editor,
279                        void *file_baton,
280                        const char *base_checksum,
281                        svn_txdelta_stream_open_func_t open_func,
282                        void *open_baton,
283                        apr_pool_t *scratch_pool)
284 {
285   struct file_baton *fb = file_baton;
286   struct edit_baton *eb = fb->edit_baton;
287 
288   SVN_ERR(write_indent(eb, scratch_pool));
289   SVN_ERR(svn_stream_printf(eb->out, scratch_pool,
290                             "apply_textdelta_stream : %s\n",
291                             base_checksum));
292 
293   if (eb->wrapped_editor)
294     SVN_ERR(eb->wrapped_editor->apply_textdelta_stream(eb->wrapped_editor,
295                                                        fb->wrapped_file_baton,
296                                                        base_checksum,
297                                                        open_func, open_baton,
298                                                        scratch_pool));
299 
300   return SVN_NO_ERROR;
301 }
302 
303 static svn_error_t *
close_file(void * file_baton,const char * text_checksum,apr_pool_t * pool)304 close_file(void *file_baton,
305            const char *text_checksum,
306            apr_pool_t *pool)
307 {
308   struct file_baton *fb = file_baton;
309   struct edit_baton *eb = fb->edit_baton;
310 
311   eb->indent_level--;
312 
313   SVN_ERR(write_indent(eb, pool));
314   SVN_ERR(svn_stream_printf(eb->out, pool, "close_file : %s\n",
315                             text_checksum));
316 
317   if (eb->wrapped_editor)
318     SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_file_baton,
319                                            text_checksum, pool));
320 
321   return SVN_NO_ERROR;
322 }
323 
324 static svn_error_t *
absent_file(const char * path,void * file_baton,apr_pool_t * pool)325 absent_file(const char *path,
326             void *file_baton,
327             apr_pool_t *pool)
328 {
329   struct file_baton *fb = file_baton;
330   struct edit_baton *eb = fb->edit_baton;
331 
332   SVN_ERR(write_indent(eb, pool));
333   SVN_ERR(svn_stream_printf(eb->out, pool, "absent_file : %s\n", path));
334 
335   if (eb->wrapped_editor)
336     SVN_ERR(eb->wrapped_editor->absent_file(path, fb->wrapped_file_baton,
337                                             pool));
338 
339   return SVN_NO_ERROR;
340 }
341 
342 static svn_error_t *
close_directory(void * dir_baton,apr_pool_t * pool)343 close_directory(void *dir_baton,
344                 apr_pool_t *pool)
345 {
346   struct dir_baton *db = dir_baton;
347   struct edit_baton *eb = db->edit_baton;
348 
349   eb->indent_level--;
350   SVN_ERR(write_indent(eb, pool));
351   SVN_ERR(svn_stream_printf(eb->out, pool, "close_directory\n"));
352 
353   if (eb->wrapped_editor)
354     SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_dir_baton,
355                                                 pool));
356 
357   return SVN_NO_ERROR;
358 }
359 
360 static svn_error_t *
absent_directory(const char * path,void * dir_baton,apr_pool_t * pool)361 absent_directory(const char *path,
362                  void *dir_baton,
363                  apr_pool_t *pool)
364 {
365   struct dir_baton *db = dir_baton;
366   struct edit_baton *eb = db->edit_baton;
367 
368   SVN_ERR(write_indent(eb, pool));
369   SVN_ERR(svn_stream_printf(eb->out, pool, "absent_directory : %s\n",
370                             path));
371 
372   if (eb->wrapped_editor)
373     SVN_ERR(eb->wrapped_editor->absent_directory(path, db->wrapped_dir_baton,
374                                                  pool));
375 
376   return SVN_NO_ERROR;
377 }
378 
379 static svn_error_t *
change_file_prop(void * file_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)380 change_file_prop(void *file_baton,
381                  const char *name,
382                  const svn_string_t *value,
383                  apr_pool_t *pool)
384 {
385   struct file_baton *fb = file_baton;
386   struct edit_baton *eb = fb->edit_baton;
387 
388   SVN_ERR(write_indent(eb, pool));
389   SVN_ERR(svn_stream_printf(eb->out, pool, "change_file_prop : %s -> %s\n",
390                             name, value ? value->data : "<deleted>"));
391 
392   if (eb->wrapped_editor)
393     SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_file_baton,
394                                                  name,
395                                                  value,
396                                                  pool));
397 
398   return SVN_NO_ERROR;
399 }
400 
401 static svn_error_t *
change_dir_prop(void * dir_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)402 change_dir_prop(void *dir_baton,
403                 const char *name,
404                 const svn_string_t *value,
405                 apr_pool_t *pool)
406 {
407   struct dir_baton *db = dir_baton;
408   struct edit_baton *eb = db->edit_baton;
409 
410   SVN_ERR(write_indent(eb, pool));
411   SVN_ERR(svn_stream_printf(eb->out, pool, "change_dir_prop : %s -> %s\n",
412                             name, value ? value->data : "<deleted>"));
413 
414   if (eb->wrapped_editor)
415     SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_dir_baton,
416                                                 name,
417                                                 value,
418                                                 pool));
419 
420   return SVN_NO_ERROR;
421 }
422 
423 static svn_error_t *
close_edit(void * edit_baton,apr_pool_t * pool)424 close_edit(void *edit_baton,
425            apr_pool_t *pool)
426 {
427   struct edit_baton *eb = edit_baton;
428 
429   SVN_ERR(write_indent(eb, pool));
430   SVN_ERR(svn_stream_printf(eb->out, pool, "close_edit\n"));
431 
432   if (eb->wrapped_editor)
433     SVN_ERR(eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool));
434 
435   return SVN_NO_ERROR;
436 }
437 
438 static svn_error_t *
abort_edit(void * edit_baton,apr_pool_t * pool)439 abort_edit(void *edit_baton,
440            apr_pool_t *pool)
441 {
442   struct edit_baton *eb = edit_baton;
443 
444   SVN_ERR(write_indent(eb, pool));
445   SVN_ERR(svn_stream_printf(eb->out, pool, "abort_edit\n"));
446 
447   if (eb->wrapped_editor)
448     SVN_ERR(eb->wrapped_editor->abort_edit(eb->wrapped_edit_baton, pool));
449 
450   return SVN_NO_ERROR;
451 }
452 
453 svn_error_t *
svn_delta__get_debug_editor(const svn_delta_editor_t ** editor,void ** edit_baton,const svn_delta_editor_t * wrapped_editor,void * wrapped_edit_baton,const char * prefix,apr_pool_t * pool)454 svn_delta__get_debug_editor(const svn_delta_editor_t **editor,
455                             void **edit_baton,
456                             const svn_delta_editor_t *wrapped_editor,
457                             void *wrapped_edit_baton,
458                             const char *prefix,
459                             apr_pool_t *pool)
460 {
461   svn_delta_editor_t *tree_editor = svn_delta_default_editor(pool);
462   struct edit_baton *eb = apr_palloc(pool, sizeof(*eb));
463   apr_file_t *errfp;
464   svn_stream_t *out;
465 
466   apr_status_t apr_err = apr_file_open_stdout(&errfp, pool);
467   if (apr_err)
468     return svn_error_wrap_apr(apr_err, "Problem opening stderr");
469 
470   out = svn_stream_from_aprfile2(errfp, TRUE, pool);
471 
472   tree_editor->set_target_revision = set_target_revision;
473   tree_editor->open_root = open_root;
474   tree_editor->delete_entry = delete_entry;
475   tree_editor->add_directory = add_directory;
476   tree_editor->open_directory = open_directory;
477   tree_editor->change_dir_prop = change_dir_prop;
478   tree_editor->close_directory = close_directory;
479   tree_editor->absent_directory = absent_directory;
480   tree_editor->add_file = add_file;
481   tree_editor->open_file = open_file;
482   tree_editor->apply_textdelta = apply_textdelta;
483   tree_editor->apply_textdelta_stream = apply_textdelta_stream;
484   tree_editor->change_file_prop = change_file_prop;
485   tree_editor->close_file = close_file;
486   tree_editor->absent_file = absent_file;
487   tree_editor->close_edit = close_edit;
488   tree_editor->abort_edit = abort_edit;
489 
490   eb->wrapped_editor = wrapped_editor;
491   eb->wrapped_edit_baton = wrapped_edit_baton;
492   eb->out = out;
493   eb->indent_level = 0;
494   /* This is DBG_FLAG from ../libsvn_subr/debug.c */
495   eb->prefix = apr_pstrcat(pool, "DBG: ", prefix, SVN_VA_NULL);
496 
497   *editor = tree_editor;
498   *edit_baton = eb;
499 
500   return SVN_NO_ERROR;
501 }
502