1251881Speter/* 2251881Speter * depth_filter_editor.c -- provide a svn_delta_editor_t which wraps 3251881Speter * another editor and provides depth-based filtering 4251881Speter * 5251881Speter * ==================================================================== 6251881Speter * Licensed to the Apache Software Foundation (ASF) under one 7251881Speter * or more contributor license agreements. See the NOTICE file 8251881Speter * distributed with this work for additional information 9251881Speter * regarding copyright ownership. The ASF licenses this file 10251881Speter * to you under the Apache License, Version 2.0 (the 11251881Speter * "License"); you may not use this file except in compliance 12251881Speter * with the License. You may obtain a copy of the License at 13251881Speter * 14251881Speter * http://www.apache.org/licenses/LICENSE-2.0 15251881Speter * 16251881Speter * Unless required by applicable law or agreed to in writing, 17251881Speter * software distributed under the License is distributed on an 18251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 19251881Speter * KIND, either express or implied. See the License for the 20251881Speter * specific language governing permissions and limitations 21251881Speter * under the License. 22251881Speter * ==================================================================== 23251881Speter */ 24251881Speter 25251881Speter#include "svn_delta.h" 26251881Speter 27251881Speter 28251881Speter/*** Batons, and the Toys That Create Them ***/ 29251881Speter 30251881Speterstruct edit_baton 31251881Speter{ 32251881Speter /* The editor/baton we're wrapping. */ 33251881Speter const svn_delta_editor_t *wrapped_editor; 34251881Speter void *wrapped_edit_baton; 35251881Speter 36251881Speter /* The depth to which we are limiting the drive of the wrapped 37251881Speter editor/baton. */ 38251881Speter svn_depth_t requested_depth; 39251881Speter 40251881Speter /* Does the wrapped editor/baton have an explicit target (in the 41251881Speter anchor/target sense of the word)? */ 42251881Speter svn_boolean_t has_target; 43251881Speter}; 44251881Speter 45251881Speterstruct node_baton 46251881Speter{ 47251881Speter /* TRUE iff this node was filtered out -- that is, not allowed to 48251881Speter pass through to the wrapped editor -- by virtue of not appearing 49251881Speter at a depth in the tree that was "inside" the requested depth. Of 50251881Speter course, any children of this node will be deeper still, and so 51251881Speter will also be filtered out for the same reason. */ 52251881Speter svn_boolean_t filtered; 53251881Speter 54251881Speter /* Pointer to the edit_baton. */ 55251881Speter void *edit_baton; 56251881Speter 57251881Speter /* The real node baton we're wrapping. May be a directory or file 58251881Speter baton; we don't care. */ 59251881Speter void *wrapped_baton; 60251881Speter 61251881Speter /* The calculated depth (in terms of counted, stacked, integral 62251881Speter deepnesses) of this node. If the node is a directory, this value 63251881Speter is 1 greater than the value of the same on its parent directory; 64251881Speter if a file, it is equal to its parent directory's depth value. */ 65251881Speter int dir_depth; 66251881Speter}; 67251881Speter 68251881Speter/* Allocate and return a new node_baton structure, populated via the 69251881Speter the input to this helper function. */ 70251881Speterstatic struct node_baton * 71251881Spetermake_node_baton(void *edit_baton, 72251881Speter svn_boolean_t filtered, 73251881Speter int dir_depth, 74251881Speter apr_pool_t *pool) 75251881Speter{ 76251881Speter struct node_baton *b = apr_palloc(pool, sizeof(*b)); 77251881Speter b->edit_baton = edit_baton; 78251881Speter b->wrapped_baton = NULL; 79251881Speter b->filtered = filtered; 80251881Speter b->dir_depth = dir_depth; 81251881Speter return b; 82251881Speter} 83251881Speter 84251881Speter/* Return TRUE iff changes to immediate children of the directory 85251881Speter identified by PB, when those children are of node kind KIND, are 86251881Speter allowed by the requested depth which this editor is trying to 87251881Speter preserve. EB is the edit baton. */ 88251881Speterstatic svn_boolean_t 89251881Speterokay_to_edit(struct edit_baton *eb, 90251881Speter struct node_baton *pb, 91251881Speter svn_node_kind_t kind) 92251881Speter{ 93251881Speter int effective_depth; 94251881Speter 95251881Speter /* If we've already filter out the parent directory, we necessarily 96251881Speter are filtering out its children, too. */ 97251881Speter if (pb->filtered) 98251881Speter return FALSE; 99251881Speter 100251881Speter /* Calculate the effective depth of the parent directory. 101251881Speter 102251881Speter NOTE: "Depth" in this sense is not the same as the Subversion 103251881Speter magic depth keywords. Here, we're talking about a literal, 104251881Speter integral, stacked depth of directories. 105251881Speter 106251881Speter The root of the edit is generally depth=1, subdirectories thereof 107251881Speter depth=2, and so on. But if we have an edit target -- which means 108251881Speter that the real target of the edit operation isn't the root 109251881Speter directory, but is instead some immediate child thereof -- we have 110251881Speter to adjust our calculated effected depth such that the target 111251881Speter itself is depth=1 (as are its siblings, which we trust aren't 112251881Speter present in the edit at all), immediate subdirectories thereof are 113251881Speter depth=2, and so on. 114251881Speter */ 115251881Speter effective_depth = pb->dir_depth - (eb->has_target ? 1 : 0); 116251881Speter switch (eb->requested_depth) 117251881Speter { 118251881Speter case svn_depth_empty: 119251881Speter return (effective_depth <= 0); 120251881Speter case svn_depth_files: 121251881Speter return ((effective_depth <= 0) 122251881Speter || (kind == svn_node_file && effective_depth == 1)); 123251881Speter case svn_depth_immediates: 124251881Speter return (effective_depth <= 1); 125251881Speter case svn_depth_unknown: 126251881Speter case svn_depth_exclude: 127251881Speter case svn_depth_infinity: 128251881Speter /* Shouldn't reach; see svn_delta_depth_filter_editor() */ 129251881Speter default: 130251881Speter SVN_ERR_MALFUNCTION_NO_RETURN(); 131251881Speter } 132251881Speter} 133251881Speter 134251881Speter 135251881Speter/*** Editor Functions ***/ 136251881Speter 137251881Speterstatic svn_error_t * 138251881Speterset_target_revision(void *edit_baton, 139251881Speter svn_revnum_t target_revision, 140251881Speter apr_pool_t *pool) 141251881Speter{ 142251881Speter struct edit_baton *eb = edit_baton; 143251881Speter 144251881Speter /* Nothing depth-y to filter here. */ 145251881Speter return eb->wrapped_editor->set_target_revision(eb->wrapped_edit_baton, 146251881Speter target_revision, pool); 147251881Speter} 148251881Speter 149251881Speterstatic svn_error_t * 150251881Speteropen_root(void *edit_baton, 151251881Speter svn_revnum_t base_revision, 152251881Speter apr_pool_t *pool, 153251881Speter void **root_baton) 154251881Speter{ 155251881Speter struct edit_baton *eb = edit_baton; 156251881Speter struct node_baton *b; 157251881Speter 158251881Speter /* The root node always gets through cleanly. */ 159251881Speter b = make_node_baton(edit_baton, FALSE, 1, pool); 160251881Speter SVN_ERR(eb->wrapped_editor->open_root(eb->wrapped_edit_baton, base_revision, 161251881Speter pool, &b->wrapped_baton)); 162251881Speter 163251881Speter *root_baton = b; 164251881Speter return SVN_NO_ERROR; 165251881Speter} 166251881Speter 167251881Speterstatic svn_error_t * 168251881Speterdelete_entry(const char *path, 169251881Speter svn_revnum_t base_revision, 170251881Speter void *parent_baton, 171251881Speter apr_pool_t *pool) 172251881Speter{ 173251881Speter struct node_baton *pb = parent_baton; 174251881Speter struct edit_baton *eb = pb->edit_baton; 175251881Speter 176251881Speter /* ### FIXME: We don't know the type of the entry, which ordinarily 177251881Speter doesn't matter, but is a key (*the* key, in fact) distinction 178251881Speter between depth "files" and depths "immediates". If the server is 179251881Speter telling us to delete a subdirectory and our requested depth was 180251881Speter "immediates", that's fine; if our requested depth was "files", 181251881Speter though, this deletion shouldn't survive filtering. For now, 182251881Speter we'll claim to our helper function that the to-be-deleted thing 183251881Speter is a file because that's the conservative route to take. */ 184251881Speter if (okay_to_edit(eb, pb, svn_node_file)) 185251881Speter SVN_ERR(eb->wrapped_editor->delete_entry(path, base_revision, 186251881Speter pb->wrapped_baton, pool)); 187251881Speter 188251881Speter return SVN_NO_ERROR; 189251881Speter} 190251881Speter 191251881Speterstatic svn_error_t * 192251881Speteradd_directory(const char *path, 193251881Speter void *parent_baton, 194251881Speter const char *copyfrom_path, 195251881Speter svn_revnum_t copyfrom_revision, 196251881Speter apr_pool_t *pool, 197251881Speter void **child_baton) 198251881Speter{ 199251881Speter struct node_baton *pb = parent_baton; 200251881Speter struct edit_baton *eb = pb->edit_baton; 201251881Speter struct node_baton *b = NULL; 202251881Speter 203251881Speter /* Check for sufficient depth. */ 204251881Speter if (okay_to_edit(eb, pb, svn_node_dir)) 205251881Speter { 206251881Speter b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool); 207251881Speter SVN_ERR(eb->wrapped_editor->add_directory(path, pb->wrapped_baton, 208251881Speter copyfrom_path, 209251881Speter copyfrom_revision, 210251881Speter pool, &b->wrapped_baton)); 211251881Speter } 212251881Speter else 213251881Speter { 214251881Speter b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool); 215251881Speter } 216251881Speter 217251881Speter *child_baton = b; 218251881Speter return SVN_NO_ERROR; 219251881Speter} 220251881Speter 221251881Speterstatic svn_error_t * 222251881Speteropen_directory(const char *path, 223251881Speter void *parent_baton, 224251881Speter svn_revnum_t base_revision, 225251881Speter apr_pool_t *pool, 226251881Speter void **child_baton) 227251881Speter{ 228251881Speter struct node_baton *pb = parent_baton; 229251881Speter struct edit_baton *eb = pb->edit_baton; 230251881Speter struct node_baton *b; 231251881Speter 232251881Speter /* Check for sufficient depth. */ 233251881Speter if (okay_to_edit(eb, pb, svn_node_dir)) 234251881Speter { 235251881Speter b = make_node_baton(eb, FALSE, pb->dir_depth + 1, pool); 236251881Speter SVN_ERR(eb->wrapped_editor->open_directory(path, pb->wrapped_baton, 237251881Speter base_revision, pool, 238251881Speter &b->wrapped_baton)); 239251881Speter } 240251881Speter else 241251881Speter { 242251881Speter b = make_node_baton(eb, TRUE, pb->dir_depth + 1, pool); 243251881Speter } 244251881Speter 245251881Speter *child_baton = b; 246251881Speter return SVN_NO_ERROR; 247251881Speter} 248251881Speter 249251881Speterstatic svn_error_t * 250251881Speteradd_file(const char *path, 251251881Speter void *parent_baton, 252251881Speter const char *copyfrom_path, 253251881Speter svn_revnum_t copyfrom_revision, 254251881Speter apr_pool_t *pool, 255251881Speter void **child_baton) 256251881Speter{ 257251881Speter struct node_baton *pb = parent_baton; 258251881Speter struct edit_baton *eb = pb->edit_baton; 259251881Speter struct node_baton *b = NULL; 260251881Speter 261251881Speter /* Check for sufficient depth. */ 262251881Speter if (okay_to_edit(eb, pb, svn_node_file)) 263251881Speter { 264251881Speter b = make_node_baton(eb, FALSE, pb->dir_depth, pool); 265251881Speter SVN_ERR(eb->wrapped_editor->add_file(path, pb->wrapped_baton, 266251881Speter copyfrom_path, copyfrom_revision, 267251881Speter pool, &b->wrapped_baton)); 268251881Speter } 269251881Speter else 270251881Speter { 271251881Speter b = make_node_baton(eb, TRUE, pb->dir_depth, pool); 272251881Speter } 273251881Speter 274251881Speter *child_baton = b; 275251881Speter return SVN_NO_ERROR; 276251881Speter} 277251881Speter 278251881Speterstatic svn_error_t * 279251881Speteropen_file(const char *path, 280251881Speter void *parent_baton, 281251881Speter svn_revnum_t base_revision, 282251881Speter apr_pool_t *pool, 283251881Speter void **child_baton) 284251881Speter{ 285251881Speter struct node_baton *pb = parent_baton; 286251881Speter struct edit_baton *eb = pb->edit_baton; 287251881Speter struct node_baton *b; 288251881Speter 289251881Speter /* Check for sufficient depth. */ 290251881Speter if (okay_to_edit(eb, pb, svn_node_file)) 291251881Speter { 292251881Speter b = make_node_baton(eb, FALSE, pb->dir_depth, pool); 293251881Speter SVN_ERR(eb->wrapped_editor->open_file(path, pb->wrapped_baton, 294251881Speter base_revision, pool, 295251881Speter &b->wrapped_baton)); 296251881Speter } 297251881Speter else 298251881Speter { 299251881Speter b = make_node_baton(eb, TRUE, pb->dir_depth, pool); 300251881Speter } 301251881Speter 302251881Speter *child_baton = b; 303251881Speter return SVN_NO_ERROR; 304251881Speter} 305251881Speter 306251881Speterstatic svn_error_t * 307251881Speterapply_textdelta(void *file_baton, 308251881Speter const char *base_checksum, 309251881Speter apr_pool_t *pool, 310251881Speter svn_txdelta_window_handler_t *handler, 311251881Speter void **handler_baton) 312251881Speter{ 313251881Speter struct node_baton *fb = file_baton; 314251881Speter struct edit_baton *eb = fb->edit_baton; 315251881Speter 316251881Speter /* For filtered files, we just consume the textdelta. */ 317251881Speter if (fb->filtered) 318251881Speter { 319251881Speter *handler = svn_delta_noop_window_handler; 320251881Speter *handler_baton = NULL; 321251881Speter } 322251881Speter else 323251881Speter { 324251881Speter SVN_ERR(eb->wrapped_editor->apply_textdelta(fb->wrapped_baton, 325251881Speter base_checksum, pool, 326251881Speter handler, handler_baton)); 327251881Speter } 328251881Speter return SVN_NO_ERROR; 329251881Speter} 330251881Speter 331251881Speterstatic svn_error_t * 332251881Speterclose_file(void *file_baton, 333251881Speter const char *text_checksum, 334251881Speter apr_pool_t *pool) 335251881Speter{ 336251881Speter struct node_baton *fb = file_baton; 337251881Speter struct edit_baton *eb = fb->edit_baton; 338251881Speter 339251881Speter /* Don't close filtered files. */ 340251881Speter if (! fb->filtered) 341251881Speter SVN_ERR(eb->wrapped_editor->close_file(fb->wrapped_baton, 342251881Speter text_checksum, pool)); 343251881Speter 344251881Speter return SVN_NO_ERROR; 345251881Speter} 346251881Speter 347251881Speterstatic svn_error_t * 348251881Speterabsent_file(const char *path, 349251881Speter void *parent_baton, 350251881Speter apr_pool_t *pool) 351251881Speter{ 352251881Speter struct node_baton *pb = parent_baton; 353251881Speter struct edit_baton *eb = pb->edit_baton; 354251881Speter 355251881Speter /* Don't report absent items in filtered directories. */ 356251881Speter if (! pb->filtered) 357251881Speter SVN_ERR(eb->wrapped_editor->absent_file(path, pb->wrapped_baton, pool)); 358251881Speter 359251881Speter return SVN_NO_ERROR; 360251881Speter} 361251881Speter 362251881Speterstatic svn_error_t * 363251881Speterclose_directory(void *dir_baton, 364251881Speter apr_pool_t *pool) 365251881Speter{ 366251881Speter struct node_baton *db = dir_baton; 367251881Speter struct edit_baton *eb = db->edit_baton; 368251881Speter 369251881Speter /* Don't close filtered directories. */ 370251881Speter if (! db->filtered) 371251881Speter SVN_ERR(eb->wrapped_editor->close_directory(db->wrapped_baton, pool)); 372251881Speter 373251881Speter return SVN_NO_ERROR; 374251881Speter} 375251881Speter 376251881Speterstatic svn_error_t * 377251881Speterabsent_directory(const char *path, 378251881Speter void *parent_baton, 379251881Speter apr_pool_t *pool) 380251881Speter{ 381251881Speter struct node_baton *pb = parent_baton; 382251881Speter struct edit_baton *eb = pb->edit_baton; 383251881Speter 384251881Speter /* Don't report absent items in filtered directories. */ 385251881Speter if (! pb->filtered) 386251881Speter SVN_ERR(eb->wrapped_editor->absent_directory(path, pb->wrapped_baton, 387251881Speter pool)); 388251881Speter 389251881Speter return SVN_NO_ERROR; 390251881Speter} 391251881Speter 392251881Speterstatic svn_error_t * 393251881Speterchange_file_prop(void *file_baton, 394251881Speter const char *name, 395251881Speter const svn_string_t *value, 396251881Speter apr_pool_t *pool) 397251881Speter{ 398251881Speter struct node_baton *fb = file_baton; 399251881Speter struct edit_baton *eb = fb->edit_baton; 400251881Speter 401251881Speter /* No propchanges on filtered files. */ 402251881Speter if (! fb->filtered) 403251881Speter SVN_ERR(eb->wrapped_editor->change_file_prop(fb->wrapped_baton, 404251881Speter name, value, pool)); 405251881Speter 406251881Speter return SVN_NO_ERROR; 407251881Speter} 408251881Speter 409251881Speterstatic svn_error_t * 410251881Speterchange_dir_prop(void *dir_baton, 411251881Speter const char *name, 412251881Speter const svn_string_t *value, 413251881Speter apr_pool_t *pool) 414251881Speter{ 415251881Speter struct node_baton *db = dir_baton; 416251881Speter struct edit_baton *eb = db->edit_baton; 417251881Speter 418251881Speter /* No propchanges on filtered nodes. */ 419251881Speter if (! db->filtered) 420251881Speter SVN_ERR(eb->wrapped_editor->change_dir_prop(db->wrapped_baton, 421251881Speter name, value, pool)); 422251881Speter 423251881Speter return SVN_NO_ERROR; 424251881Speter} 425251881Speter 426251881Speterstatic svn_error_t * 427251881Speterclose_edit(void *edit_baton, 428251881Speter apr_pool_t *pool) 429251881Speter{ 430251881Speter struct edit_baton *eb = edit_baton; 431251881Speter return eb->wrapped_editor->close_edit(eb->wrapped_edit_baton, pool); 432251881Speter} 433251881Speter 434251881Spetersvn_error_t * 435251881Spetersvn_delta_depth_filter_editor(const svn_delta_editor_t **editor, 436251881Speter void **edit_baton, 437251881Speter const svn_delta_editor_t *wrapped_editor, 438251881Speter void *wrapped_edit_baton, 439251881Speter svn_depth_t requested_depth, 440251881Speter svn_boolean_t has_target, 441251881Speter apr_pool_t *pool) 442251881Speter{ 443251881Speter svn_delta_editor_t *depth_filter_editor; 444251881Speter struct edit_baton *eb; 445251881Speter 446251881Speter /* Easy out: if the caller wants infinite depth, there's nothing to 447251881Speter filter, so just return the editor we were supposed to wrap. And 448251881Speter if they've asked for an unknown depth, we can't possibly know 449251881Speter what that means, so why bother? */ 450251881Speter if ((requested_depth == svn_depth_unknown) 451251881Speter || (requested_depth == svn_depth_infinity)) 452251881Speter { 453251881Speter *editor = wrapped_editor; 454251881Speter *edit_baton = wrapped_edit_baton; 455251881Speter return SVN_NO_ERROR; 456251881Speter } 457251881Speter 458251881Speter depth_filter_editor = svn_delta_default_editor(pool); 459251881Speter depth_filter_editor->set_target_revision = set_target_revision; 460251881Speter depth_filter_editor->open_root = open_root; 461251881Speter depth_filter_editor->delete_entry = delete_entry; 462251881Speter depth_filter_editor->add_directory = add_directory; 463251881Speter depth_filter_editor->open_directory = open_directory; 464251881Speter depth_filter_editor->change_dir_prop = change_dir_prop; 465251881Speter depth_filter_editor->close_directory = close_directory; 466251881Speter depth_filter_editor->absent_directory = absent_directory; 467251881Speter depth_filter_editor->add_file = add_file; 468251881Speter depth_filter_editor->open_file = open_file; 469251881Speter depth_filter_editor->apply_textdelta = apply_textdelta; 470251881Speter depth_filter_editor->change_file_prop = change_file_prop; 471251881Speter depth_filter_editor->close_file = close_file; 472251881Speter depth_filter_editor->absent_file = absent_file; 473251881Speter depth_filter_editor->close_edit = close_edit; 474251881Speter 475251881Speter eb = apr_palloc(pool, sizeof(*eb)); 476251881Speter eb->wrapped_editor = wrapped_editor; 477251881Speter eb->wrapped_edit_baton = wrapped_edit_baton; 478251881Speter eb->has_target = has_target; 479251881Speter eb->requested_depth = requested_depth; 480251881Speter 481251881Speter *editor = depth_filter_editor; 482251881Speter *edit_baton = eb; 483251881Speter 484251881Speter return SVN_NO_ERROR; 485251881Speter} 486