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