📄 editor.c
字号:
/* * editor.c : Driving and consuming an editor across an svn connection * * ==================================================================== * Copyright (c) 2000-2006 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#define APR_WANT_STRFUNC#include <apr_want.h>#include <apr_general.h>#include <apr_strings.h>#include <apr_md5.h>#include <assert.h>#include "svn_types.h"#include "svn_string.h"#include "svn_error.h"#include "svn_path.h"#include "svn_delta.h"#include "svn_ra_svn.h"#include "svn_pools.h"#include "svn_private_config.h"#include "ra_svn.h"/* * Both the client and server in the svn protocol need to drive and * consume editors. For a commit, the client drives and the server * consumes; for an update/switch/status/diff, the server drives and * the client consumes. This file provides a generic framework for * marshalling and unmarshalling editor operations over an svn * connection; both ends are useful for both server and client. */typedef struct { svn_ra_svn_conn_t *conn; svn_ra_svn_edit_callback callback; /* Called on successful completion. */ void *callback_baton; int next_token;} ra_svn_edit_baton_t;/* Works for both directories and files. */typedef struct { svn_ra_svn_conn_t *conn; apr_pool_t *pool; ra_svn_edit_baton_t *eb; const char *token;} ra_svn_baton_t;typedef struct { const svn_delta_editor_t *editor; void *edit_baton; apr_hash_t *tokens; svn_boolean_t *aborted; apr_pool_t *pool; svn_boolean_t for_replay;} ra_svn_driver_state_t;typedef struct { const char *token; void *baton; svn_error_t *err; /* Tracks delayed errors. */ apr_pool_t *pool;} ra_svn_token_entry_t;/* --- CONSUMING AN EDITOR BY PASSING EDIT OPERATIONS OVER THE NET --- */static const char *make_token(char type, ra_svn_edit_baton_t *eb, apr_pool_t *pool){ return apr_psprintf(pool, "%c%d", type, eb->next_token++);}static ra_svn_baton_t *ra_svn_make_baton(svn_ra_svn_conn_t *conn, apr_pool_t *pool, ra_svn_edit_baton_t *eb, const char *token){ ra_svn_baton_t *b; b = apr_palloc(pool, sizeof(*b)); b->conn = conn; b->pool = pool; b->eb = eb; b->token = token; return b;}static svn_error_t *ra_svn_target_rev(void *edit_baton, svn_revnum_t rev, apr_pool_t *pool){ ra_svn_edit_baton_t *eb = edit_baton; SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "target-rev", "r", rev)); SVN_ERR(svn_ra_svn_read_cmd_response(eb->conn, pool, "")); return SVN_NO_ERROR;}static svn_error_t *ra_svn_open_root(void *edit_baton, svn_revnum_t rev, apr_pool_t *pool, void **root_baton){ ra_svn_edit_baton_t *eb = edit_baton; const char *token = make_token('d', eb, pool); SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "open-root", "(?r)c", rev, token)); SVN_ERR(svn_ra_svn_read_cmd_response(eb->conn, pool, "")); *root_baton = ra_svn_make_baton(eb->conn, pool, eb, token); return SVN_NO_ERROR;}static svn_error_t *ra_svn_delete_entry(const char *path, svn_revnum_t rev, void *parent_baton, apr_pool_t *pool){ ra_svn_baton_t *b = parent_baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "delete-entry", "c(?r)c", path, rev, b->token)); SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, pool, "")); return SVN_NO_ERROR;}static svn_error_t *ra_svn_add_dir(const char *path, void *parent_baton, const char *copy_path, svn_revnum_t copy_rev, apr_pool_t *pool, void **child_baton){ ra_svn_baton_t *b = parent_baton; const char *token = make_token('d', b->eb, pool); assert((copy_path && SVN_IS_VALID_REVNUM(copy_rev)) || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev))); SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "add-dir", "ccc(?cr)", path, b->token, token, copy_path, copy_rev)); SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, pool, "")); *child_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); return SVN_NO_ERROR;}static svn_error_t *ra_svn_open_dir(const char *path, void *parent_baton, svn_revnum_t rev, apr_pool_t *pool, void **child_baton){ ra_svn_baton_t *b = parent_baton; const char *token = make_token('d', b->eb, pool); SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "open-dir", "ccc(?r)", path, b->token, token, rev)); SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, pool, "")); *child_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); return SVN_NO_ERROR;}static svn_error_t *ra_svn_change_dir_prop(void *dir_baton, const char *name, const svn_string_t *value, apr_pool_t *pool){ ra_svn_baton_t *b = dir_baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "change-dir-prop", "cc(?s)", b->token, name, value)); return SVN_NO_ERROR;}static svn_error_t *ra_svn_close_dir(void *dir_baton, apr_pool_t *pool){ ra_svn_baton_t *b = dir_baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "close-dir", "c", b->token)); SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, pool, "")); return SVN_NO_ERROR;}static svn_error_t *ra_svn_add_file(const char *path, void *parent_baton, const char *copy_path, svn_revnum_t copy_rev, apr_pool_t *pool, void **file_baton){ ra_svn_baton_t *b = parent_baton; const char *token = make_token('c', b->eb, pool); assert((copy_path && SVN_IS_VALID_REVNUM(copy_rev)) || (!copy_path && !SVN_IS_VALID_REVNUM(copy_rev))); SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "add-file", "ccc(?cr)", path, b->token, token, copy_path, copy_rev)); *file_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); return SVN_NO_ERROR;}static svn_error_t *ra_svn_open_file(const char *path, void *parent_baton, svn_revnum_t rev, apr_pool_t *pool, void **file_baton){ ra_svn_baton_t *b = parent_baton; const char *token = make_token('c', b->eb, pool); SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "open-file", "ccc(?r)", path, b->token, token, rev)); *file_baton = ra_svn_make_baton(b->conn, pool, b->eb, token); return SVN_NO_ERROR;}static svn_error_t *ra_svn_svndiff_handler(void *baton, const char *data, apr_size_t *len){ ra_svn_baton_t *b = baton; svn_string_t str; str.data = data; str.len = *len; return svn_ra_svn_write_string(b->conn, b->pool, &str);}static svn_error_t *ra_svn_svndiff_close_handler(void *baton){ ra_svn_baton_t *b = baton; SVN_ERR(svn_ra_svn_write_cstring(b->conn, b->pool, "")); return SVN_NO_ERROR;}static svn_error_t *ra_svn_apply_textdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *wh, void **wh_baton){ ra_svn_baton_t *b = file_baton; svn_stream_t *diff_stream; /* Tell the other side we're starting a text delta. */ SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "apply-textdelta", "c(?c)", b->token, base_checksum)); /* Transform the window stream to an svndiff stream. Reuse the * file baton for the stream handler, since it has all the * needed information. */ diff_stream = svn_stream_create(b, pool); svn_stream_set_write(diff_stream, ra_svn_svndiff_handler); svn_stream_set_close(diff_stream, ra_svn_svndiff_close_handler); if (svn_ra_svn_has_capability(b->conn, SVN_RA_SVN_CAP_SVNDIFF1)) svn_txdelta_to_svndiff2(wh, wh_baton, diff_stream, 1, pool); else svn_txdelta_to_svndiff2(wh, wh_baton, diff_stream, 0, pool); return SVN_NO_ERROR;} static svn_error_t *ra_svn_change_file_prop(void *file_baton, const char *name, const svn_string_t *value, apr_pool_t *pool){ ra_svn_baton_t *b = file_baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "change-file-prop", "cc(?s)", b->token, name, value)); return SVN_NO_ERROR;}static svn_error_t *ra_svn_close_file(void *file_baton, const char *text_checksum, apr_pool_t *pool){ ra_svn_baton_t *b = file_baton; SVN_ERR(svn_ra_svn_write_cmd(b->conn, pool, "close-file", "c(?c)", b->token, text_checksum)); SVN_ERR(svn_ra_svn_read_cmd_response(b->conn, pool, "")); return SVN_NO_ERROR;}static svn_error_t *ra_svn_close_edit(void *edit_baton, apr_pool_t *pool){ ra_svn_edit_baton_t *eb = edit_baton; SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "close-edit", "")); SVN_ERR(svn_ra_svn_read_cmd_response(eb->conn, pool, "")); if (eb->callback) SVN_ERR(eb->callback(eb->callback_baton)); return SVN_NO_ERROR;}static svn_error_t *ra_svn_abort_edit(void *edit_baton, apr_pool_t *pool){ ra_svn_edit_baton_t *eb = edit_baton; SVN_ERR(svn_ra_svn_write_cmd(eb->conn, pool, "abort-edit", "")); SVN_ERR(svn_ra_svn_read_cmd_response(eb->conn, pool, "")); return SVN_NO_ERROR;}void svn_ra_svn_get_editor(const svn_delta_editor_t **editor, void **edit_baton, svn_ra_svn_conn_t *conn, apr_pool_t *pool, svn_ra_svn_edit_callback callback, void *callback_baton){ svn_delta_editor_t *ra_svn_editor; ra_svn_edit_baton_t *eb; if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE)) { svn_ra_svn__get_editorp(editor, edit_baton, conn, pool, callback, callback_baton); return; } eb = apr_palloc(pool, sizeof(*eb)); eb->conn = conn; eb->callback = callback; eb->callback_baton = callback_baton; eb->next_token = 0; ra_svn_editor = svn_delta_default_editor(pool); ra_svn_editor->set_target_revision = ra_svn_target_rev; ra_svn_editor->open_root = ra_svn_open_root; ra_svn_editor->delete_entry = ra_svn_delete_entry; ra_svn_editor->add_directory = ra_svn_add_dir; ra_svn_editor->open_directory = ra_svn_open_dir; ra_svn_editor->change_dir_prop = ra_svn_change_dir_prop; ra_svn_editor->close_directory = ra_svn_close_dir; ra_svn_editor->add_file = ra_svn_add_file; ra_svn_editor->open_file = ra_svn_open_file; ra_svn_editor->apply_textdelta = ra_svn_apply_textdelta; ra_svn_editor->change_file_prop = ra_svn_change_file_prop; ra_svn_editor->close_file = ra_svn_close_file; ra_svn_editor->close_edit = ra_svn_close_edit; ra_svn_editor->abort_edit = ra_svn_abort_edit; *editor = ra_svn_editor; *edit_baton = eb;}/* --- DRIVING AN EDITOR --- */static apr_status_t clear_token_err(void *arg){ ra_svn_token_entry_t *entry = arg; svn_error_clear(entry->err); return APR_SUCCESS;}/* Store a token entry. The token string will be copied into pool. */static ra_svn_token_entry_t *store_token(ra_svn_driver_state_t *ds, void *baton, const char *token, apr_pool_t *pool){ ra_svn_token_entry_t *entry; entry = apr_palloc(pool, sizeof(*entry)); entry->token = apr_pstrdup(pool, token); entry->baton = baton; entry->err = NULL; entry->pool = pool; apr_hash_set(ds->tokens, entry->token, APR_HASH_KEY_STRING, entry); apr_pool_cleanup_register(pool, entry, clear_token_err, apr_pool_cleanup_null); return entry;}static svn_error_t *lookup_token(ra_svn_driver_state_t *ds, const char *token, ra_svn_token_entry_t **entry, apr_pool_t *pool){ *entry = apr_hash_get(ds->tokens, token, APR_HASH_KEY_STRING); if (!*entry) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Invalid file or dir token during edit"));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -