1251881Speter/* 2251881Speter * editorp.c : Driving and consuming an editor across an svn connection 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#define APR_WANT_STRFUNC 27251881Speter#include <apr_want.h> 28251881Speter#include <apr_general.h> 29251881Speter#include <apr_strings.h> 30251881Speter 31251881Speter#include "svn_hash.h" 32251881Speter#include "svn_types.h" 33251881Speter#include "svn_string.h" 34251881Speter#include "svn_error.h" 35251881Speter#include "svn_delta.h" 36251881Speter#include "svn_dirent_uri.h" 37251881Speter#include "svn_ra_svn.h" 38251881Speter#include "svn_path.h" 39251881Speter#include "svn_pools.h" 40251881Speter#include "svn_private_config.h" 41251881Speter 42251881Speter#include "private/svn_fspath.h" 43251881Speter#include "private/svn_editor.h" 44251881Speter 45251881Speter#include "ra_svn.h" 46251881Speter 47251881Speter/* 48251881Speter * Both the client and server in the svn protocol need to drive and 49251881Speter * consume editors. For a commit, the client drives and the server 50251881Speter * consumes; for an update/switch/status/diff, the server drives and 51251881Speter * the client consumes. This file provides a generic framework for 52251881Speter * marshalling and unmarshalling editor operations over an svn 53251881Speter * connection; both ends are useful for both server and client. 54251881Speter */ 55251881Speter 56251881Spetertypedef struct ra_svn_edit_baton_t { 57251881Speter svn_ra_svn_conn_t *conn; 58251881Speter svn_ra_svn_edit_callback callback; /* Called on successful completion. */ 59251881Speter void *callback_baton; 60251881Speter int next_token; 61251881Speter svn_boolean_t got_status; 62251881Speter} ra_svn_edit_baton_t; 63251881Speter 64251881Speter/* Works for both directories and files. */ 65251881Spetertypedef struct ra_svn_baton_t { 66251881Speter svn_ra_svn_conn_t *conn; 67251881Speter apr_pool_t *pool; 68251881Speter ra_svn_edit_baton_t *eb; 69251881Speter const char *token; 70251881Speter} ra_svn_baton_t; 71251881Speter 72251881Spetertypedef struct ra_svn_driver_state_t { 73251881Speter const svn_delta_editor_t *editor; 74251881Speter void *edit_baton; 75251881Speter apr_hash_t *tokens; 76251881Speter svn_boolean_t *aborted; 77251881Speter svn_boolean_t done; 78251881Speter apr_pool_t *pool; 79251881Speter apr_pool_t *file_pool; 80251881Speter int file_refs; 81251881Speter svn_boolean_t for_replay; 82251881Speter} ra_svn_driver_state_t; 83251881Speter 84251881Speter/* Works for both directories and files; however, the pool handling is 85251881Speter different for files. To save space during commits (where file 86251881Speter batons generally last until the end of the commit), token entries 87251881Speter for files are all created in a single reference-counted pool (the 88251881Speter file_pool member of the driver state structure), which is cleared 89251881Speter at close_file time when the reference count hits zero. So the pool 90251881Speter field in this structure is vestigial for files, and we use it for a 91251881Speter different purpose instead: at apply-textdelta time, we set it to a 92251881Speter subpool of the file pool, which is destroyed in textdelta-end. */ 93251881Spetertypedef struct ra_svn_token_entry_t { 94299742Sdim svn_string_t *token; 95251881Speter void *baton; 96251881Speter svn_boolean_t is_file; 97251881Speter svn_stream_t *dstream; /* svndiff stream for apply_textdelta */ 98251881Speter apr_pool_t *pool; 99251881Speter} ra_svn_token_entry_t; 100251881Speter 101251881Speter/* --- CONSUMING AN EDITOR BY PASSING EDIT OPERATIONS OVER THE NET --- */ 102251881Speter 103251881Speterstatic const char *make_token(char type, ra_svn_edit_baton_t *eb, 104251881Speter apr_pool_t *pool) 105251881Speter{ 106251881Speter return apr_psprintf(pool, "%c%d", type, eb->next_token++); 107251881Speter} 108251881Speter 109251881Speterstatic ra_svn_baton_t *ra_svn_make_baton(svn_ra_svn_conn_t *conn, 110251881Speter apr_pool_t *pool, 111251881Speter ra_svn_edit_baton_t *eb, 112251881Speter const char *token) 113251881Speter{ 114251881Speter ra_svn_baton_t *b; 115251881Speter 116251881Speter b = apr_palloc(pool, sizeof(*b)); 117251881Speter b->conn = conn; 118251881Speter b->pool = pool; 119251881Speter b->eb = eb; 120251881Speter b->token = token; 121251881Speter return b; 122251881Speter} 123251881Speter 124251881Speter/* Check for an early error status report from the consumer. If we 125251881Speter * get one, abort the edit and return the error. */ 126251881Speterstatic svn_error_t * 127251881Spetercheck_for_error_internal(ra_svn_edit_baton_t *eb, apr_pool_t *pool) 128251881Speter{ 129299742Sdim svn_boolean_t available; 130251881Speter SVN_ERR_ASSERT(!eb->got_status); 131251881Speter 132251881Speter /* reset TX counter */ 133251881Speter eb->conn->written_since_error_check = 0; 134251881Speter 135251881Speter /* if we weren't asked to always check, wait for at least the next TX */ 136251881Speter eb->conn->may_check_for_error = eb->conn->error_check_interval == 0; 137251881Speter 138251881Speter /* any incoming data? */ 139299742Sdim SVN_ERR(svn_ra_svn__data_available(eb->conn, &available)); 140299742Sdim if (available) 141251881Speter { 142251881Speter eb->got_status = TRUE; 143251881Speter SVN_ERR(svn_ra_svn__write_cmd_abort_edit(eb->conn, pool)); 144251881Speter SVN_ERR(svn_ra_svn__read_cmd_response(eb->conn, pool, "")); 145251881Speter /* We shouldn't get here if the consumer is doing its job. */ 146251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 147251881Speter _("Successful edit status returned too soon")); 148251881Speter } 149251881Speter return SVN_NO_ERROR; 150251881Speter} 151251881Speter 152251881Speterstatic svn_error_t * 153251881Spetercheck_for_error(ra_svn_edit_baton_t *eb, apr_pool_t *pool) 154251881Speter{ 155251881Speter return eb->conn->may_check_for_error 156251881Speter ? check_for_error_internal(eb, pool) 157251881Speter : SVN_NO_ERROR; 158251881Speter} 159251881Speter 160251881Speterstatic svn_error_t *ra_svn_target_rev(void *edit_baton, svn_revnum_t rev, 161251881Speter apr_pool_t *pool) 162251881Speter{ 163251881Speter ra_svn_edit_baton_t *eb = edit_baton; 164251881Speter 165251881Speter SVN_ERR(check_for_error(eb, pool)); 166251881Speter SVN_ERR(svn_ra_svn__write_cmd_target_rev(eb->conn, pool, rev)); 167251881Speter return SVN_NO_ERROR; 168251881Speter} 169251881Speter 170251881Speterstatic svn_error_t *ra_svn_open_root(void *edit_baton, svn_revnum_t rev, 171251881Speter apr_pool_t *pool, void **root_baton) 172251881Speter{ 173251881Speter ra_svn_edit_baton_t *eb = edit_baton; 174251881Speter const char *token = make_token('d', eb, pool); 175251881Speter 176251881Speter SVN_ERR(check_for_error(eb, pool)); 177251881Speter SVN_ERR(svn_ra_svn__write_cmd_open_root(eb->conn, pool, rev, token)); 178251881Speter *root_baton = ra_svn_make_baton(eb->conn, pool, eb, token); 179251881Speter return SVN_NO_ERROR; 180251881Speter} 181251881Speter 182251881Speterstatic svn_error_t *ra_svn_delete_entry(const char *path, svn_revnum_t rev, 183251881Speter void *parent_baton, apr_pool_t *pool) 184251881Speter{ 185251881Speter ra_svn_baton_t *b = parent_baton; 186251881Speter 187251881Speter SVN_ERR(check_for_error(b->eb, pool)); 188251881Speter SVN_ERR(svn_ra_svn__write_cmd_delete_entry(b->conn, pool, 189251881Speter path, rev, b->token)); 190251881Speter return SVN_NO_ERROR; 191251881Speter} 192251881Speter 193251881Speterstatic svn_error_t *ra_svn_add_dir(const char *path, void *parent_baton, 194251881Speter const char *copy_path, 195251881Speter svn_revnum_t copy_rev, 196251881Speter apr_pool_t *pool, void **child_baton) 197251881Speter{ 198251881Speter ra_svn_baton_t *b = parent_baton; 199251881Speter const char *token = make_token('d', b->eb, pool); 200251881Speter 201251881Speter SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev)) 202251881Speter || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev))); 203251881Speter SVN_ERR(check_for_error(b->eb, pool)); 204251881Speter SVN_ERR(svn_ra_svn__write_cmd_add_dir(b->conn, pool, path, b->token, 205251881Speter token, copy_path, copy_rev)); 206251881Speter *child_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); 207251881Speter return SVN_NO_ERROR; 208251881Speter} 209251881Speter 210251881Speterstatic svn_error_t *ra_svn_open_dir(const char *path, void *parent_baton, 211251881Speter svn_revnum_t rev, apr_pool_t *pool, 212251881Speter void **child_baton) 213251881Speter{ 214251881Speter ra_svn_baton_t *b = parent_baton; 215251881Speter const char *token = make_token('d', b->eb, pool); 216251881Speter 217251881Speter SVN_ERR(check_for_error(b->eb, pool)); 218251881Speter SVN_ERR(svn_ra_svn__write_cmd_open_dir(b->conn, pool, path, b->token, 219251881Speter token, rev)); 220251881Speter *child_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); 221251881Speter return SVN_NO_ERROR; 222251881Speter} 223251881Speter 224251881Speterstatic svn_error_t *ra_svn_change_dir_prop(void *dir_baton, const char *name, 225251881Speter const svn_string_t *value, 226251881Speter apr_pool_t *pool) 227251881Speter{ 228251881Speter ra_svn_baton_t *b = dir_baton; 229251881Speter 230251881Speter SVN_ERR(check_for_error(b->eb, pool)); 231251881Speter SVN_ERR(svn_ra_svn__write_cmd_change_dir_prop(b->conn, pool, b->token, 232251881Speter name, value)); 233251881Speter return SVN_NO_ERROR; 234251881Speter} 235251881Speter 236251881Speterstatic svn_error_t *ra_svn_close_dir(void *dir_baton, apr_pool_t *pool) 237251881Speter{ 238251881Speter ra_svn_baton_t *b = dir_baton; 239251881Speter 240251881Speter SVN_ERR(check_for_error(b->eb, pool)); 241251881Speter SVN_ERR(svn_ra_svn__write_cmd_close_dir(b->conn, pool, b->token)); 242251881Speter return SVN_NO_ERROR; 243251881Speter} 244251881Speter 245251881Speterstatic svn_error_t *ra_svn_absent_dir(const char *path, 246251881Speter void *parent_baton, apr_pool_t *pool) 247251881Speter{ 248251881Speter ra_svn_baton_t *b = parent_baton; 249251881Speter 250251881Speter /* Avoid sending an unknown command if the other end doesn't support 251251881Speter absent-dir. */ 252251881Speter if (! svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_ABSENT_ENTRIES)) 253251881Speter return SVN_NO_ERROR; 254251881Speter 255251881Speter SVN_ERR(check_for_error(b->eb, pool)); 256251881Speter SVN_ERR(svn_ra_svn__write_cmd_absent_dir(b->conn, pool, path, b->token)); 257251881Speter return SVN_NO_ERROR; 258251881Speter} 259251881Speter 260251881Speterstatic svn_error_t *ra_svn_add_file(const char *path, 261251881Speter void *parent_baton, 262251881Speter const char *copy_path, 263251881Speter svn_revnum_t copy_rev, 264251881Speter apr_pool_t *pool, 265251881Speter void **file_baton) 266251881Speter{ 267251881Speter ra_svn_baton_t *b = parent_baton; 268251881Speter const char *token = make_token('c', b->eb, pool); 269251881Speter 270251881Speter SVN_ERR_ASSERT((copy_path && SVN_IS_VALID_REVNUM(copy_rev)) 271251881Speter || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev))); 272251881Speter SVN_ERR(check_for_error(b->eb, pool)); 273251881Speter SVN_ERR(svn_ra_svn__write_cmd_add_file(b->conn, pool, path, b->token, 274251881Speter token, copy_path, copy_rev)); 275251881Speter *file_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); 276251881Speter return SVN_NO_ERROR; 277251881Speter} 278251881Speter 279251881Speterstatic svn_error_t *ra_svn_open_file(const char *path, 280251881Speter void *parent_baton, 281251881Speter svn_revnum_t rev, 282251881Speter apr_pool_t *pool, 283251881Speter void **file_baton) 284251881Speter{ 285251881Speter ra_svn_baton_t *b = parent_baton; 286251881Speter const char *token = make_token('c', b->eb, pool); 287251881Speter 288251881Speter SVN_ERR(check_for_error(b->eb, b->pool)); 289251881Speter SVN_ERR(svn_ra_svn__write_cmd_open_file(b->conn, pool, path, b->token, 290251881Speter token, rev)); 291251881Speter *file_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); 292251881Speter return SVN_NO_ERROR; 293251881Speter} 294251881Speter 295251881Speterstatic svn_error_t *ra_svn_svndiff_handler(void *baton, const char *data, 296251881Speter apr_size_t *len) 297251881Speter{ 298251881Speter ra_svn_baton_t *b = baton; 299251881Speter svn_string_t str; 300251881Speter 301251881Speter SVN_ERR(check_for_error(b->eb, b->pool)); 302251881Speter str.data = data; 303251881Speter str.len = *len; 304251881Speter return svn_ra_svn__write_cmd_textdelta_chunk(b->conn, b->pool, 305251881Speter b->token, &str); 306251881Speter} 307251881Speter 308251881Speterstatic svn_error_t *ra_svn_svndiff_close_handler(void *baton) 309251881Speter{ 310251881Speter ra_svn_baton_t *b = baton; 311251881Speter 312251881Speter SVN_ERR(check_for_error(b->eb, b->pool)); 313251881Speter SVN_ERR(svn_ra_svn__write_cmd_textdelta_end(b->conn, b->pool, b->token)); 314251881Speter return SVN_NO_ERROR; 315251881Speter} 316251881Speter 317251881Speterstatic svn_error_t *ra_svn_apply_textdelta(void *file_baton, 318251881Speter const char *base_checksum, 319251881Speter apr_pool_t *pool, 320251881Speter svn_txdelta_window_handler_t *wh, 321251881Speter void **wh_baton) 322251881Speter{ 323251881Speter ra_svn_baton_t *b = file_baton; 324251881Speter svn_stream_t *diff_stream; 325251881Speter 326251881Speter /* Tell the other side we're starting a text delta. */ 327251881Speter SVN_ERR(check_for_error(b->eb, pool)); 328251881Speter SVN_ERR(svn_ra_svn__write_cmd_apply_textdelta(b->conn, pool, b->token, 329251881Speter base_checksum)); 330251881Speter 331251881Speter /* Transform the window stream to an svndiff stream. Reuse the 332251881Speter * file baton for the stream handler, since it has all the 333251881Speter * needed information. */ 334251881Speter diff_stream = svn_stream_create(b, pool); 335251881Speter svn_stream_set_write(diff_stream, ra_svn_svndiff_handler); 336251881Speter svn_stream_set_close(diff_stream, ra_svn_svndiff_close_handler); 337251881Speter 338251881Speter /* If the connection does not support SVNDIFF1 or if we don't want to use 339251881Speter * compression, use the non-compressing "version 0" implementation */ 340251881Speter if ( svn_ra_svn_compression_level(b->conn) > 0 341251881Speter && svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_SVNDIFF1)) 342251881Speter svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, 1, 343251881Speter b->conn->compression_level, pool); 344251881Speter else 345251881Speter svn_txdelta_to_svndiff3(wh, wh_baton, diff_stream, 0, 346251881Speter b->conn->compression_level, pool); 347251881Speter return SVN_NO_ERROR; 348251881Speter} 349251881Speter 350251881Speterstatic svn_error_t *ra_svn_change_file_prop(void *file_baton, 351251881Speter const char *name, 352251881Speter const svn_string_t *value, 353251881Speter apr_pool_t *pool) 354251881Speter{ 355251881Speter ra_svn_baton_t *b = file_baton; 356251881Speter 357251881Speter SVN_ERR(check_for_error(b->eb, pool)); 358251881Speter SVN_ERR(svn_ra_svn__write_cmd_change_file_prop(b->conn, pool, 359251881Speter b->token, name, value)); 360251881Speter return SVN_NO_ERROR; 361251881Speter} 362251881Speter 363251881Speterstatic svn_error_t *ra_svn_close_file(void *file_baton, 364251881Speter const char *text_checksum, 365251881Speter apr_pool_t *pool) 366251881Speter{ 367251881Speter ra_svn_baton_t *b = file_baton; 368251881Speter 369251881Speter SVN_ERR(check_for_error(b->eb, pool)); 370251881Speter SVN_ERR(svn_ra_svn__write_cmd_close_file(b->conn, pool, 371251881Speter b->token, text_checksum)); 372251881Speter return SVN_NO_ERROR; 373251881Speter} 374251881Speter 375251881Speterstatic svn_error_t *ra_svn_absent_file(const char *path, 376251881Speter void *parent_baton, apr_pool_t *pool) 377251881Speter{ 378251881Speter ra_svn_baton_t *b = parent_baton; 379251881Speter 380251881Speter /* Avoid sending an unknown command if the other end doesn't support 381251881Speter absent-file. */ 382251881Speter if (! svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_ABSENT_ENTRIES)) 383251881Speter return SVN_NO_ERROR; 384251881Speter 385251881Speter SVN_ERR(check_for_error(b->eb, pool)); 386251881Speter SVN_ERR(svn_ra_svn__write_cmd_absent_file(b->conn, pool, path, b->token)); 387251881Speter return SVN_NO_ERROR; 388251881Speter} 389251881Speter 390251881Speterstatic svn_error_t *ra_svn_close_edit(void *edit_baton, apr_pool_t *pool) 391251881Speter{ 392251881Speter ra_svn_edit_baton_t *eb = edit_baton; 393251881Speter svn_error_t *err; 394251881Speter 395251881Speter SVN_ERR_ASSERT(!eb->got_status); 396251881Speter eb->got_status = TRUE; 397251881Speter SVN_ERR(svn_ra_svn__write_cmd_close_edit(eb->conn, pool)); 398299742Sdim err = svn_error_trace(svn_ra_svn__read_cmd_response(eb->conn, pool, "")); 399251881Speter if (err) 400251881Speter { 401299742Sdim return svn_error_compose_create( 402299742Sdim err, 403299742Sdim svn_error_trace( 404299742Sdim svn_ra_svn__write_cmd_abort_edit(eb->conn, pool))); 405251881Speter } 406251881Speter if (eb->callback) 407251881Speter SVN_ERR(eb->callback(eb->callback_baton)); 408251881Speter return SVN_NO_ERROR; 409251881Speter} 410251881Speter 411251881Speterstatic svn_error_t *ra_svn_abort_edit(void *edit_baton, apr_pool_t *pool) 412251881Speter{ 413251881Speter ra_svn_edit_baton_t *eb = edit_baton; 414251881Speter 415251881Speter if (eb->got_status) 416251881Speter return SVN_NO_ERROR; 417251881Speter SVN_ERR(svn_ra_svn__write_cmd_abort_edit(eb->conn, pool)); 418251881Speter SVN_ERR(svn_ra_svn__read_cmd_response(eb->conn, pool, "")); 419251881Speter return SVN_NO_ERROR; 420251881Speter} 421251881Speter 422251881Spetervoid svn_ra_svn_get_editor(const svn_delta_editor_t **editor, 423251881Speter void **edit_baton, svn_ra_svn_conn_t *conn, 424251881Speter apr_pool_t *pool, 425251881Speter svn_ra_svn_edit_callback callback, 426251881Speter void *callback_baton) 427251881Speter{ 428251881Speter svn_delta_editor_t *ra_svn_editor = svn_delta_default_editor(pool); 429251881Speter ra_svn_edit_baton_t *eb; 430251881Speter 431251881Speter eb = apr_palloc(pool, sizeof(*eb)); 432251881Speter eb->conn = conn; 433251881Speter eb->callback = callback; 434251881Speter eb->callback_baton = callback_baton; 435251881Speter eb->next_token = 0; 436251881Speter eb->got_status = FALSE; 437251881Speter 438251881Speter ra_svn_editor->set_target_revision = ra_svn_target_rev; 439251881Speter ra_svn_editor->open_root = ra_svn_open_root; 440251881Speter ra_svn_editor->delete_entry = ra_svn_delete_entry; 441251881Speter ra_svn_editor->add_directory = ra_svn_add_dir; 442251881Speter ra_svn_editor->open_directory = ra_svn_open_dir; 443251881Speter ra_svn_editor->change_dir_prop = ra_svn_change_dir_prop; 444251881Speter ra_svn_editor->close_directory = ra_svn_close_dir; 445251881Speter ra_svn_editor->absent_directory = ra_svn_absent_dir; 446251881Speter ra_svn_editor->add_file = ra_svn_add_file; 447251881Speter ra_svn_editor->open_file = ra_svn_open_file; 448251881Speter ra_svn_editor->apply_textdelta = ra_svn_apply_textdelta; 449251881Speter ra_svn_editor->change_file_prop = ra_svn_change_file_prop; 450251881Speter ra_svn_editor->close_file = ra_svn_close_file; 451251881Speter ra_svn_editor->absent_file = ra_svn_absent_file; 452251881Speter ra_svn_editor->close_edit = ra_svn_close_edit; 453251881Speter ra_svn_editor->abort_edit = ra_svn_abort_edit; 454251881Speter 455251881Speter *editor = ra_svn_editor; 456251881Speter *edit_baton = eb; 457251881Speter 458251881Speter svn_error_clear(svn_editor__insert_shims(editor, edit_baton, *editor, 459251881Speter *edit_baton, NULL, NULL, 460251881Speter conn->shim_callbacks, 461251881Speter pool, pool)); 462251881Speter} 463251881Speter 464251881Speter/* --- DRIVING AN EDITOR --- */ 465251881Speter 466251881Speter/* Store a token entry. The token string will be copied into pool. */ 467251881Speterstatic ra_svn_token_entry_t *store_token(ra_svn_driver_state_t *ds, 468299742Sdim void *baton, 469299742Sdim svn_string_t *token, 470251881Speter svn_boolean_t is_file, 471251881Speter apr_pool_t *pool) 472251881Speter{ 473251881Speter ra_svn_token_entry_t *entry; 474251881Speter 475251881Speter entry = apr_palloc(pool, sizeof(*entry)); 476299742Sdim entry->token = svn_string_dup(token, pool); 477251881Speter entry->baton = baton; 478251881Speter entry->is_file = is_file; 479251881Speter entry->dstream = NULL; 480251881Speter entry->pool = pool; 481299742Sdim 482299742Sdim apr_hash_set(ds->tokens, entry->token->data, entry->token->len, entry); 483299742Sdim 484251881Speter return entry; 485251881Speter} 486251881Speter 487299742Sdimstatic svn_error_t *lookup_token(ra_svn_driver_state_t *ds, 488299742Sdim svn_string_t *token, 489251881Speter svn_boolean_t is_file, 490251881Speter ra_svn_token_entry_t **entry) 491251881Speter{ 492299742Sdim *entry = apr_hash_get(ds->tokens, token->data, token->len); 493251881Speter if (!*entry || (*entry)->is_file != is_file) 494251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 495251881Speter _("Invalid file or dir token during edit")); 496251881Speter return SVN_NO_ERROR; 497251881Speter} 498251881Speter 499251881Speterstatic svn_error_t *ra_svn_handle_target_rev(svn_ra_svn_conn_t *conn, 500251881Speter apr_pool_t *pool, 501251881Speter const apr_array_header_t *params, 502251881Speter ra_svn_driver_state_t *ds) 503251881Speter{ 504251881Speter svn_revnum_t rev; 505251881Speter 506251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev)); 507251881Speter SVN_CMD_ERR(ds->editor->set_target_revision(ds->edit_baton, rev, pool)); 508251881Speter return SVN_NO_ERROR; 509251881Speter} 510251881Speter 511251881Speterstatic svn_error_t *ra_svn_handle_open_root(svn_ra_svn_conn_t *conn, 512251881Speter apr_pool_t *pool, 513251881Speter const apr_array_header_t *params, 514251881Speter ra_svn_driver_state_t *ds) 515251881Speter{ 516251881Speter svn_revnum_t rev; 517251881Speter apr_pool_t *subpool; 518299742Sdim svn_string_t *token; 519251881Speter void *root_baton; 520251881Speter 521299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)s", &rev, &token)); 522251881Speter subpool = svn_pool_create(ds->pool); 523251881Speter SVN_CMD_ERR(ds->editor->open_root(ds->edit_baton, rev, subpool, 524251881Speter &root_baton)); 525251881Speter store_token(ds, root_baton, token, FALSE, subpool); 526251881Speter return SVN_NO_ERROR; 527251881Speter} 528251881Speter 529251881Speterstatic svn_error_t *ra_svn_handle_delete_entry(svn_ra_svn_conn_t *conn, 530251881Speter apr_pool_t *pool, 531251881Speter const apr_array_header_t *params, 532251881Speter ra_svn_driver_state_t *ds) 533251881Speter{ 534299742Sdim const char *path; 535299742Sdim svn_string_t *token; 536251881Speter svn_revnum_t rev; 537251881Speter ra_svn_token_entry_t *entry; 538251881Speter 539299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)s", 540251881Speter &path, &rev, &token)); 541251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 542251881Speter path = svn_relpath_canonicalize(path, pool); 543251881Speter SVN_CMD_ERR(ds->editor->delete_entry(path, rev, entry->baton, pool)); 544251881Speter return SVN_NO_ERROR; 545251881Speter} 546251881Speter 547251881Speterstatic svn_error_t *ra_svn_handle_add_dir(svn_ra_svn_conn_t *conn, 548251881Speter apr_pool_t *pool, 549251881Speter const apr_array_header_t *params, 550251881Speter ra_svn_driver_state_t *ds) 551251881Speter{ 552299742Sdim const char *path, *copy_path; 553299742Sdim svn_string_t *token, *child_token; 554251881Speter svn_revnum_t copy_rev; 555251881Speter ra_svn_token_entry_t *entry; 556251881Speter apr_pool_t *subpool; 557251881Speter void *child_baton; 558251881Speter 559299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, 560251881Speter &child_token, ©_path, ©_rev)); 561251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 562251881Speter subpool = svn_pool_create(entry->pool); 563251881Speter path = svn_relpath_canonicalize(path, pool); 564251881Speter 565251881Speter /* Some operations pass COPY_PATH as a full URL (commits, etc.). 566251881Speter Others (replay, e.g.) deliver an fspath. That's ... annoying. */ 567251881Speter if (copy_path) 568251881Speter { 569251881Speter if (svn_path_is_url(copy_path)) 570251881Speter copy_path = svn_uri_canonicalize(copy_path, pool); 571251881Speter else 572251881Speter copy_path = svn_fspath__canonicalize(copy_path, pool); 573251881Speter } 574251881Speter 575251881Speter SVN_CMD_ERR(ds->editor->add_directory(path, entry->baton, copy_path, 576251881Speter copy_rev, subpool, &child_baton)); 577251881Speter store_token(ds, child_baton, child_token, FALSE, subpool); 578251881Speter return SVN_NO_ERROR; 579251881Speter} 580251881Speter 581251881Speterstatic svn_error_t *ra_svn_handle_open_dir(svn_ra_svn_conn_t *conn, 582251881Speter apr_pool_t *pool, 583251881Speter const apr_array_header_t *params, 584251881Speter ra_svn_driver_state_t *ds) 585251881Speter{ 586299742Sdim const char *path; 587299742Sdim svn_string_t *token, *child_token; 588251881Speter svn_revnum_t rev; 589251881Speter ra_svn_token_entry_t *entry; 590251881Speter apr_pool_t *subpool; 591251881Speter void *child_baton; 592251881Speter 593299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, 594251881Speter &child_token, &rev)); 595251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 596251881Speter subpool = svn_pool_create(entry->pool); 597251881Speter path = svn_relpath_canonicalize(path, pool); 598251881Speter SVN_CMD_ERR(ds->editor->open_directory(path, entry->baton, rev, subpool, 599251881Speter &child_baton)); 600251881Speter store_token(ds, child_baton, child_token, FALSE, subpool); 601251881Speter return SVN_NO_ERROR; 602251881Speter} 603251881Speter 604251881Speterstatic svn_error_t *ra_svn_handle_change_dir_prop(svn_ra_svn_conn_t *conn, 605251881Speter apr_pool_t *pool, 606251881Speter const apr_array_header_t *params, 607251881Speter ra_svn_driver_state_t *ds) 608251881Speter{ 609299742Sdim svn_string_t *token; 610299742Sdim const char *name; 611251881Speter svn_string_t *value; 612251881Speter ra_svn_token_entry_t *entry; 613251881Speter 614299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name, 615251881Speter &value)); 616251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 617251881Speter SVN_CMD_ERR(ds->editor->change_dir_prop(entry->baton, name, value, 618251881Speter entry->pool)); 619251881Speter return SVN_NO_ERROR; 620251881Speter} 621251881Speter 622251881Speterstatic svn_error_t *ra_svn_handle_close_dir(svn_ra_svn_conn_t *conn, 623251881Speter apr_pool_t *pool, 624251881Speter const apr_array_header_t *params, 625251881Speter ra_svn_driver_state_t *ds) 626251881Speter{ 627299742Sdim svn_string_t *token; 628251881Speter ra_svn_token_entry_t *entry; 629251881Speter 630251881Speter /* Parse and look up the directory token. */ 631299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); 632251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 633251881Speter 634251881Speter /* Close the directory and destroy the baton. */ 635251881Speter SVN_CMD_ERR(ds->editor->close_directory(entry->baton, pool)); 636299742Sdim apr_hash_set(ds->tokens, token->data, token->len, NULL); 637251881Speter svn_pool_destroy(entry->pool); 638251881Speter return SVN_NO_ERROR; 639251881Speter} 640251881Speter 641251881Speterstatic svn_error_t *ra_svn_handle_absent_dir(svn_ra_svn_conn_t *conn, 642251881Speter apr_pool_t *pool, 643251881Speter const apr_array_header_t *params, 644251881Speter ra_svn_driver_state_t *ds) 645251881Speter{ 646251881Speter const char *path; 647299742Sdim svn_string_t *token; 648251881Speter ra_svn_token_entry_t *entry; 649251881Speter 650251881Speter /* Parse parameters and look up the directory token. */ 651299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); 652251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 653251881Speter 654251881Speter /* Call the editor. */ 655251881Speter SVN_CMD_ERR(ds->editor->absent_directory(path, entry->baton, pool)); 656251881Speter return SVN_NO_ERROR; 657251881Speter} 658251881Speter 659251881Speterstatic svn_error_t *ra_svn_handle_add_file(svn_ra_svn_conn_t *conn, 660251881Speter apr_pool_t *pool, 661251881Speter const apr_array_header_t *params, 662251881Speter ra_svn_driver_state_t *ds) 663251881Speter{ 664299742Sdim const char *path, *copy_path; 665299742Sdim svn_string_t *token, *file_token; 666251881Speter svn_revnum_t copy_rev; 667251881Speter ra_svn_token_entry_t *entry, *file_entry; 668251881Speter 669299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?cr)", &path, &token, 670251881Speter &file_token, ©_path, ©_rev)); 671251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 672251881Speter ds->file_refs++; 673251881Speter 674299742Sdim /* The PATH should be canonical .. but never trust incoming data. */ 675299742Sdim if (!svn_relpath_is_canonical(path)) 676299742Sdim path = svn_relpath_canonicalize(path, pool); 677299742Sdim 678251881Speter /* Some operations pass COPY_PATH as a full URL (commits, etc.). 679251881Speter Others (replay, e.g.) deliver an fspath. That's ... annoying. */ 680251881Speter if (copy_path) 681251881Speter { 682251881Speter if (svn_path_is_url(copy_path)) 683251881Speter copy_path = svn_uri_canonicalize(copy_path, pool); 684251881Speter else 685251881Speter copy_path = svn_fspath__canonicalize(copy_path, pool); 686251881Speter } 687251881Speter 688251881Speter file_entry = store_token(ds, NULL, file_token, TRUE, ds->file_pool); 689251881Speter SVN_CMD_ERR(ds->editor->add_file(path, entry->baton, copy_path, copy_rev, 690251881Speter ds->file_pool, &file_entry->baton)); 691251881Speter return SVN_NO_ERROR; 692251881Speter} 693251881Speter 694251881Speterstatic svn_error_t *ra_svn_handle_open_file(svn_ra_svn_conn_t *conn, 695251881Speter apr_pool_t *pool, 696251881Speter const apr_array_header_t *params, 697251881Speter ra_svn_driver_state_t *ds) 698251881Speter{ 699299742Sdim const char *path; 700299742Sdim svn_string_t *token, *file_token; 701251881Speter svn_revnum_t rev; 702251881Speter ra_svn_token_entry_t *entry, *file_entry; 703251881Speter 704299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "css(?r)", &path, &token, 705251881Speter &file_token, &rev)); 706251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 707251881Speter ds->file_refs++; 708299742Sdim 709299742Sdim /* The PATH should be canonical .. but never trust incoming data. */ 710299742Sdim if (!svn_relpath_is_canonical(path)) 711299742Sdim path = svn_relpath_canonicalize(path, pool); 712299742Sdim 713251881Speter file_entry = store_token(ds, NULL, file_token, TRUE, ds->file_pool); 714251881Speter SVN_CMD_ERR(ds->editor->open_file(path, entry->baton, rev, ds->file_pool, 715251881Speter &file_entry->baton)); 716251881Speter return SVN_NO_ERROR; 717251881Speter} 718251881Speter 719251881Speterstatic svn_error_t *ra_svn_handle_apply_textdelta(svn_ra_svn_conn_t *conn, 720251881Speter apr_pool_t *pool, 721251881Speter const apr_array_header_t *params, 722251881Speter ra_svn_driver_state_t *ds) 723251881Speter{ 724299742Sdim svn_string_t *token; 725251881Speter ra_svn_token_entry_t *entry; 726251881Speter svn_txdelta_window_handler_t wh; 727251881Speter void *wh_baton; 728251881Speter char *base_checksum; 729251881Speter 730251881Speter /* Parse arguments and look up the token. */ 731299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", 732251881Speter &token, &base_checksum)); 733251881Speter SVN_ERR(lookup_token(ds, token, TRUE, &entry)); 734251881Speter if (entry->dstream) 735251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 736251881Speter _("Apply-textdelta already active")); 737251881Speter entry->pool = svn_pool_create(ds->file_pool); 738251881Speter SVN_CMD_ERR(ds->editor->apply_textdelta(entry->baton, base_checksum, 739251881Speter entry->pool, &wh, &wh_baton)); 740251881Speter entry->dstream = svn_txdelta_parse_svndiff(wh, wh_baton, TRUE, entry->pool); 741251881Speter return SVN_NO_ERROR; 742251881Speter} 743251881Speter 744251881Speterstatic svn_error_t *ra_svn_handle_textdelta_chunk(svn_ra_svn_conn_t *conn, 745251881Speter apr_pool_t *pool, 746251881Speter const apr_array_header_t *params, 747251881Speter ra_svn_driver_state_t *ds) 748251881Speter{ 749299742Sdim svn_string_t *token; 750251881Speter ra_svn_token_entry_t *entry; 751251881Speter svn_string_t *str; 752251881Speter 753251881Speter /* Parse arguments and look up the token. */ 754299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ss", &token, &str)); 755251881Speter SVN_ERR(lookup_token(ds, token, TRUE, &entry)); 756251881Speter if (!entry->dstream) 757251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 758251881Speter _("Apply-textdelta not active")); 759251881Speter SVN_CMD_ERR(svn_stream_write(entry->dstream, str->data, &str->len)); 760251881Speter return SVN_NO_ERROR; 761251881Speter} 762251881Speter 763251881Speterstatic svn_error_t *ra_svn_handle_textdelta_end(svn_ra_svn_conn_t *conn, 764251881Speter apr_pool_t *pool, 765251881Speter const apr_array_header_t *params, 766251881Speter ra_svn_driver_state_t *ds) 767251881Speter{ 768299742Sdim svn_string_t *token; 769251881Speter ra_svn_token_entry_t *entry; 770251881Speter 771251881Speter /* Parse arguments and look up the token. */ 772299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s", &token)); 773251881Speter SVN_ERR(lookup_token(ds, token, TRUE, &entry)); 774251881Speter if (!entry->dstream) 775251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 776251881Speter _("Apply-textdelta not active")); 777251881Speter SVN_CMD_ERR(svn_stream_close(entry->dstream)); 778251881Speter entry->dstream = NULL; 779251881Speter svn_pool_destroy(entry->pool); 780251881Speter return SVN_NO_ERROR; 781251881Speter} 782251881Speter 783251881Speterstatic svn_error_t *ra_svn_handle_change_file_prop(svn_ra_svn_conn_t *conn, 784251881Speter apr_pool_t *pool, 785251881Speter const apr_array_header_t *params, 786251881Speter ra_svn_driver_state_t *ds) 787251881Speter{ 788299742Sdim const char *name; 789299742Sdim svn_string_t *token, *value; 790251881Speter ra_svn_token_entry_t *entry; 791251881Speter 792299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "sc(?s)", &token, &name, 793251881Speter &value)); 794251881Speter SVN_ERR(lookup_token(ds, token, TRUE, &entry)); 795251881Speter SVN_CMD_ERR(ds->editor->change_file_prop(entry->baton, name, value, pool)); 796251881Speter return SVN_NO_ERROR; 797251881Speter} 798251881Speter 799251881Speterstatic svn_error_t *ra_svn_handle_close_file(svn_ra_svn_conn_t *conn, 800251881Speter apr_pool_t *pool, 801251881Speter const apr_array_header_t *params, 802251881Speter ra_svn_driver_state_t *ds) 803251881Speter{ 804299742Sdim svn_string_t *token; 805251881Speter ra_svn_token_entry_t *entry; 806251881Speter const char *text_checksum; 807251881Speter 808251881Speter /* Parse arguments and look up the file token. */ 809299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "s(?c)", 810251881Speter &token, &text_checksum)); 811251881Speter SVN_ERR(lookup_token(ds, token, TRUE, &entry)); 812251881Speter 813251881Speter /* Close the file and destroy the baton. */ 814251881Speter SVN_CMD_ERR(ds->editor->close_file(entry->baton, text_checksum, pool)); 815299742Sdim apr_hash_set(ds->tokens, token->data, token->len, NULL); 816251881Speter if (--ds->file_refs == 0) 817251881Speter svn_pool_clear(ds->file_pool); 818251881Speter return SVN_NO_ERROR; 819251881Speter} 820251881Speter 821251881Speterstatic svn_error_t *ra_svn_handle_absent_file(svn_ra_svn_conn_t *conn, 822251881Speter apr_pool_t *pool, 823251881Speter const apr_array_header_t *params, 824251881Speter ra_svn_driver_state_t *ds) 825251881Speter{ 826251881Speter const char *path; 827299742Sdim svn_string_t *token; 828251881Speter ra_svn_token_entry_t *entry; 829251881Speter 830251881Speter /* Parse parameters and look up the parent directory token. */ 831299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cs", &path, &token)); 832251881Speter SVN_ERR(lookup_token(ds, token, FALSE, &entry)); 833251881Speter 834251881Speter /* Call the editor. */ 835251881Speter SVN_CMD_ERR(ds->editor->absent_file(path, entry->baton, pool)); 836251881Speter return SVN_NO_ERROR; 837251881Speter} 838251881Speter 839251881Speterstatic svn_error_t *ra_svn_handle_close_edit(svn_ra_svn_conn_t *conn, 840251881Speter apr_pool_t *pool, 841251881Speter const apr_array_header_t *params, 842251881Speter ra_svn_driver_state_t *ds) 843251881Speter{ 844251881Speter SVN_CMD_ERR(ds->editor->close_edit(ds->edit_baton, pool)); 845251881Speter ds->done = TRUE; 846251881Speter if (ds->aborted) 847251881Speter *ds->aborted = FALSE; 848251881Speter return svn_ra_svn__write_cmd_response(conn, pool, ""); 849251881Speter} 850251881Speter 851251881Speterstatic svn_error_t *ra_svn_handle_abort_edit(svn_ra_svn_conn_t *conn, 852251881Speter apr_pool_t *pool, 853251881Speter const apr_array_header_t *params, 854251881Speter ra_svn_driver_state_t *ds) 855251881Speter{ 856251881Speter ds->done = TRUE; 857251881Speter if (ds->aborted) 858251881Speter *ds->aborted = TRUE; 859251881Speter SVN_CMD_ERR(ds->editor->abort_edit(ds->edit_baton, pool)); 860251881Speter return svn_ra_svn__write_cmd_response(conn, pool, ""); 861251881Speter} 862251881Speter 863251881Speterstatic svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn, 864251881Speter apr_pool_t *pool, 865251881Speter const apr_array_header_t *params, 866251881Speter ra_svn_driver_state_t *ds) 867251881Speter{ 868251881Speter if (!ds->for_replay) 869251881Speter return svn_error_createf 870251881Speter (SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, 871251881Speter _("Command 'finish-replay' invalid outside of replays")); 872251881Speter ds->done = TRUE; 873251881Speter if (ds->aborted) 874251881Speter *ds->aborted = FALSE; 875251881Speter return SVN_NO_ERROR; 876251881Speter} 877251881Speter 878251881Speterstatic const struct { 879251881Speter const char *cmd; 880251881Speter svn_error_t *(*handler)(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 881251881Speter const apr_array_header_t *params, 882251881Speter ra_svn_driver_state_t *ds); 883251881Speter} ra_svn_edit_cmds[] = { 884251881Speter { "change-file-prop", ra_svn_handle_change_file_prop }, 885251881Speter { "open-file", ra_svn_handle_open_file }, 886251881Speter { "apply-textdelta", ra_svn_handle_apply_textdelta }, 887251881Speter { "textdelta-chunk", ra_svn_handle_textdelta_chunk }, 888251881Speter { "close-file", ra_svn_handle_close_file }, 889251881Speter { "add-dir", ra_svn_handle_add_dir }, 890251881Speter { "open-dir", ra_svn_handle_open_dir }, 891251881Speter { "change-dir-prop", ra_svn_handle_change_dir_prop }, 892251881Speter { "delete-entry", ra_svn_handle_delete_entry }, 893251881Speter { "close-dir", ra_svn_handle_close_dir }, 894251881Speter { "absent-dir", ra_svn_handle_absent_dir }, 895251881Speter { "add-file", ra_svn_handle_add_file }, 896251881Speter { "textdelta-end", ra_svn_handle_textdelta_end }, 897251881Speter { "absent-file", ra_svn_handle_absent_file }, 898251881Speter { "abort-edit", ra_svn_handle_abort_edit }, 899251881Speter { "finish-replay", ra_svn_handle_finish_replay }, 900251881Speter { "target-rev", ra_svn_handle_target_rev }, 901251881Speter { "open-root", ra_svn_handle_open_root }, 902251881Speter { "close-edit", ra_svn_handle_close_edit }, 903251881Speter { NULL } 904251881Speter}; 905251881Speter 906251881Speterstatic svn_error_t *blocked_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 907251881Speter void *baton) 908251881Speter{ 909251881Speter ra_svn_driver_state_t *ds = baton; 910251881Speter const char *cmd; 911251881Speter apr_array_header_t *params; 912251881Speter 913251881Speter /* We blocked trying to send an error. Read and discard an editing 914251881Speter * command in order to avoid deadlock. */ 915251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "wl", &cmd, ¶ms)); 916251881Speter if (strcmp(cmd, "abort-edit") == 0) 917251881Speter { 918251881Speter ds->done = TRUE; 919251881Speter svn_ra_svn__set_block_handler(conn, NULL, NULL); 920251881Speter } 921251881Speter return SVN_NO_ERROR; 922251881Speter} 923251881Speter 924251881Spetersvn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, 925251881Speter apr_pool_t *pool, 926251881Speter const svn_delta_editor_t *editor, 927251881Speter void *edit_baton, 928251881Speter svn_boolean_t *aborted, 929251881Speter svn_boolean_t for_replay) 930251881Speter{ 931251881Speter ra_svn_driver_state_t state; 932251881Speter apr_pool_t *subpool = svn_pool_create(pool); 933251881Speter const char *cmd; 934251881Speter int i; 935251881Speter svn_error_t *err, *write_err; 936251881Speter apr_array_header_t *params; 937251881Speter 938251881Speter state.editor = editor; 939251881Speter state.edit_baton = edit_baton; 940251881Speter state.tokens = apr_hash_make(pool); 941251881Speter state.aborted = aborted; 942251881Speter state.done = FALSE; 943251881Speter state.pool = pool; 944251881Speter state.file_pool = svn_pool_create(pool); 945251881Speter state.file_refs = 0; 946251881Speter state.for_replay = for_replay; 947251881Speter 948251881Speter while (!state.done) 949251881Speter { 950251881Speter svn_pool_clear(subpool); 951251881Speter if (editor) 952251881Speter { 953251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, subpool, "wl", &cmd, ¶ms)); 954251881Speter for (i = 0; ra_svn_edit_cmds[i].cmd; i++) 955251881Speter if (strcmp(cmd, ra_svn_edit_cmds[i].cmd) == 0) 956251881Speter break; 957251881Speter 958251881Speter if (ra_svn_edit_cmds[i].cmd) 959251881Speter err = (*ra_svn_edit_cmds[i].handler)(conn, subpool, params, &state); 960251881Speter else if (strcmp(cmd, "failure") == 0) 961251881Speter { 962251881Speter /* While not really an editor command this can occur when 963251881Speter reporter->finish_report() fails before the first editor 964251881Speter command */ 965251881Speter if (aborted) 966251881Speter *aborted = TRUE; 967251881Speter err = svn_ra_svn__handle_failure_status(params, pool); 968251881Speter return svn_error_compose_create( 969251881Speter err, 970251881Speter editor->abort_edit(edit_baton, subpool)); 971251881Speter } 972251881Speter else 973251881Speter { 974251881Speter err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, 975251881Speter _("Unknown editor command '%s'"), cmd); 976251881Speter err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); 977251881Speter } 978251881Speter } 979251881Speter else 980251881Speter { 981251881Speter const char* command = NULL; 982251881Speter SVN_ERR(svn_ra_svn__read_command_only(conn, subpool, &command)); 983251881Speter if (strcmp(command, "close-edit") == 0) 984251881Speter { 985251881Speter state.done = TRUE; 986251881Speter if (aborted) 987251881Speter *aborted = FALSE; 988251881Speter err = svn_ra_svn__write_cmd_response(conn, pool, ""); 989251881Speter } 990251881Speter else 991251881Speter err = NULL; 992251881Speter } 993251881Speter 994251881Speter if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) 995251881Speter { 996251881Speter if (aborted) 997251881Speter *aborted = TRUE; 998251881Speter if (!state.done) 999251881Speter { 1000251881Speter /* Abort the edit and use non-blocking I/O to write the error. */ 1001251881Speter if (editor) 1002299742Sdim { 1003299742Sdim err = svn_error_compose_create( 1004299742Sdim err, 1005299742Sdim svn_error_trace(editor->abort_edit(edit_baton, 1006299742Sdim subpool))); 1007299742Sdim } 1008251881Speter svn_ra_svn__set_block_handler(conn, blocked_write, &state); 1009251881Speter } 1010251881Speter write_err = svn_ra_svn__write_cmd_failure( 1011251881Speter conn, subpool, 1012251881Speter svn_ra_svn__locate_real_error_child(err)); 1013251881Speter if (!write_err) 1014251881Speter write_err = svn_ra_svn__flush(conn, subpool); 1015251881Speter svn_ra_svn__set_block_handler(conn, NULL, NULL); 1016299742Sdim svn_error_clear(err); /* We just sent this error */ 1017251881Speter SVN_ERR(write_err); 1018251881Speter break; 1019251881Speter } 1020251881Speter SVN_ERR(err); 1021251881Speter } 1022251881Speter 1023251881Speter /* Read and discard editing commands until the edit is complete. 1024251881Speter Hopefully, the other side will call another editor command, run 1025251881Speter check_for_error, notice the error, write "abort-edit" at us, and 1026251881Speter throw the error up a few levels on its side (possibly even 1027251881Speter tossing it right back at us, which is why we can return 1028251881Speter SVN_NO_ERROR below). 1029251881Speter 1030251881Speter However, if the other side is way ahead of us, it might 1031251881Speter completely finish the edit (or sequence of edit/revprops, for 1032251881Speter "replay-range") before we send over our "failure". So we should 1033251881Speter also stop if we see "success". (Then the other side will try to 1034251881Speter interpret our "failure" as a command, which will itself fail... 1035251881Speter The net effect is that whatever error we wrote to the other side 1036251881Speter will be replaced with SVN_ERR_RA_SVN_UNKNOWN_CMD.) 1037251881Speter */ 1038251881Speter while (!state.done) 1039251881Speter { 1040251881Speter svn_pool_clear(subpool); 1041251881Speter err = svn_ra_svn__read_tuple(conn, subpool, "wl", &cmd, ¶ms); 1042251881Speter if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) 1043251881Speter { 1044251881Speter /* Other side disconnected; that's no error. */ 1045251881Speter svn_error_clear(err); 1046251881Speter svn_pool_destroy(subpool); 1047251881Speter return SVN_NO_ERROR; 1048251881Speter } 1049251881Speter svn_error_clear(err); 1050251881Speter if (strcmp(cmd, "abort-edit") == 0 1051251881Speter || strcmp(cmd, "success") == 0) 1052251881Speter state.done = TRUE; 1053251881Speter } 1054251881Speter 1055251881Speter svn_pool_destroy(subpool); 1056251881Speter return SVN_NO_ERROR; 1057251881Speter} 1058251881Speter 1059251881Spetersvn_error_t *svn_ra_svn_drive_editor(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1060251881Speter const svn_delta_editor_t *editor, 1061251881Speter void *edit_baton, 1062251881Speter svn_boolean_t *aborted) 1063251881Speter{ 1064251881Speter return svn_ra_svn_drive_editor2(conn, 1065251881Speter pool, 1066251881Speter editor, 1067251881Speter edit_baton, 1068251881Speter aborted, 1069251881Speter FALSE); 1070251881Speter} 1071