/* Like svn_ra_svn_write_cmd_failure, but also clears the given error and sets it to SVN_NO_ERROR. */ static svn_error_t * write_failure(svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_error_t **err_p) { svn_error_t *write_err = svn_ra_svn__write_cmd_failure(conn, pool, *err_p); svn_error_clear(*err_p); *err_p = SVN_NO_ERROR; return write_err; }
svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const svn_delta_editor_t *editor, void *edit_baton, svn_boolean_t *aborted, svn_boolean_t for_replay) { ra_svn_driver_state_t state; apr_pool_t *subpool = svn_pool_create(pool); const char *cmd; int i; svn_error_t *err, *write_err; apr_array_header_t *params; state.editor = editor; state.edit_baton = edit_baton; state.tokens = apr_hash_make(pool); state.aborted = aborted; state.done = FALSE; state.pool = pool; state.file_pool = svn_pool_create(pool); state.file_refs = 0; state.for_replay = for_replay; while (!state.done) { svn_pool_clear(subpool); if (editor) { SVN_ERR(svn_ra_svn__read_tuple(conn, subpool, "wl", &cmd, ¶ms)); for (i = 0; ra_svn_edit_cmds[i].cmd; i++) if (strcmp(cmd, ra_svn_edit_cmds[i].cmd) == 0) break; if (ra_svn_edit_cmds[i].cmd) err = (*ra_svn_edit_cmds[i].handler)(conn, subpool, params, &state); else if (strcmp(cmd, "failure") == 0) { /* While not really an editor command this can occur when reporter->finish_report() fails before the first editor command */ if (aborted) *aborted = TRUE; err = svn_ra_svn__handle_failure_status(params, pool); return svn_error_compose_create( err, editor->abort_edit(edit_baton, subpool)); } else { err = svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, _("Unknown editor command '%s'"), cmd); err = svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, err, NULL); } } else { const char* command = NULL; SVN_ERR(svn_ra_svn__read_command_only(conn, subpool, &command)); if (strcmp(command, "close-edit") == 0) { state.done = TRUE; if (aborted) *aborted = FALSE; err = svn_ra_svn__write_cmd_response(conn, pool, ""); } else err = NULL; } if (err && err->apr_err == SVN_ERR_RA_SVN_CMD_ERR) { if (aborted) *aborted = TRUE; if (!state.done) { /* Abort the edit and use non-blocking I/O to write the error. */ if (editor) svn_error_clear(editor->abort_edit(edit_baton, subpool)); svn_ra_svn__set_block_handler(conn, blocked_write, &state); } write_err = svn_ra_svn__write_cmd_failure( conn, subpool, svn_ra_svn__locate_real_error_child(err)); if (!write_err) write_err = svn_ra_svn__flush(conn, subpool); svn_ra_svn__set_block_handler(conn, NULL, NULL); svn_error_clear(err); SVN_ERR(write_err); break; } SVN_ERR(err); } /* Read and discard editing commands until the edit is complete. Hopefully, the other side will call another editor command, run check_for_error, notice the error, write "abort-edit" at us, and throw the error up a few levels on its side (possibly even tossing it right back at us, which is why we can return SVN_NO_ERROR below). However, if the other side is way ahead of us, it might completely finish the edit (or sequence of edit/revprops, for "replay-range") before we send over our "failure". So we should also stop if we see "success". (Then the other side will try to interpret our "failure" as a command, which will itself fail... The net effect is that whatever error we wrote to the other side will be replaced with SVN_ERR_RA_SVN_UNKNOWN_CMD.) */ while (!state.done) { svn_pool_clear(subpool); err = svn_ra_svn__read_tuple(conn, subpool, "wl", &cmd, ¶ms); if (err && err->apr_err == SVN_ERR_RA_SVN_CONNECTION_CLOSED) { /* Other side disconnected; that's no error. */ svn_error_clear(err); svn_pool_destroy(subpool); return SVN_NO_ERROR; } svn_error_clear(err); if (strcmp(cmd, "abort-edit") == 0 || strcmp(cmd, "success") == 0) state.done = TRUE; } svn_pool_destroy(subpool); return SVN_NO_ERROR; }