📄 marshal.c
字号:
/* * marshal.c : Marshalling routines for Subversion protocol * * ==================================================================== * Copyright (c) 2000-2004 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/. * ==================================================================== */#include <assert.h>#include <stdlib.h>#define APR_WANT_STRFUNC#include <apr_want.h>#include <apr_general.h>#include <apr_lib.h>#include <apr_strings.h>#include <apr_network_io.h>#include <apr_poll.h>#include "svn_types.h"#include "svn_string.h"#include "svn_error.h"#include "svn_pools.h"#include "svn_ra_svn.h"#include "svn_private_config.h"#include "ra_svn.h"#define svn_iswhitespace(c) ((c) == ' ' || (c) == '\n')/* --- CONNECTION INITIALIZATION --- */svn_ra_svn_conn_t *svn_ra_svn_create_conn(apr_socket_t *sock, apr_file_t *in_file, apr_file_t *out_file, apr_pool_t *pool){ svn_ra_svn_conn_t *conn = apr_palloc(pool, sizeof(*conn)); assert((sock && !in_file && !out_file) || (!sock && in_file && out_file)); conn->sock = sock; conn->in_file = in_file; conn->out_file = out_file; conn->read_ptr = conn->read_buf; conn->read_end = conn->read_buf; conn->write_pos = 0; conn->block_handler = NULL; conn->block_baton = NULL; conn->capabilities = apr_hash_make(pool); conn->pool = pool; return conn;}svn_error_t *svn_ra_svn_set_capabilities(svn_ra_svn_conn_t *conn, apr_array_header_t *list){ int i; svn_ra_svn_item_t *item; const char *word; for (i = 0; i < list->nelts; i++) { item = &APR_ARRAY_IDX(list, i, svn_ra_svn_item_t); if (item->kind != SVN_RA_SVN_WORD) return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, _("Capability entry is not a word")); word = apr_pstrdup(conn->pool, item->u.word); apr_hash_set(conn->capabilities, word, APR_HASH_KEY_STRING, word); } return SVN_NO_ERROR;}svn_boolean_t svn_ra_svn_has_capability(svn_ra_svn_conn_t *conn, const char *capability){ return (apr_hash_get(conn->capabilities, capability, APR_HASH_KEY_STRING) != NULL);}voidsvn_ra_svn__set_block_handler(svn_ra_svn_conn_t *conn, ra_svn_block_handler_t handler, void *baton){ apr_interval_time_t interval = (handler) ? 0 : -1; conn->block_handler = handler; conn->block_baton = baton; if (conn->sock) apr_socket_timeout_set(conn->sock, interval); else apr_file_pipe_timeout_set(conn->out_file, interval);}svn_boolean_t svn_ra_svn__input_waiting(svn_ra_svn_conn_t *conn, apr_pool_t *pool){ apr_pollfd_t pfd; int n; if (conn->sock) { pfd.desc_type = APR_POLL_SOCKET; pfd.desc.s = conn->sock; } else { pfd.desc_type = APR_POLL_FILE; pfd.desc.f = conn->in_file; } pfd.p = pool; pfd.reqevents = APR_POLLIN;#ifndef AS400 return ((apr_poll(&pfd, 1, &n, 0) == APR_SUCCESS) && n);#else /* OS400 requires a pool argument for apr_poll(). */ return ((apr_poll(&pfd, 1, &n, 0, pool) == APR_SUCCESS) && n);#endif}/* --- WRITE BUFFER MANAGEMENT --- *//* Write bytes into the write buffer until either the write buffer is * full or we reach END. */static const char *writebuf_push(svn_ra_svn_conn_t *conn, const char *data, const char *end){ apr_ssize_t buflen, copylen; buflen = sizeof(conn->write_buf) - conn->write_pos; copylen = (buflen < end - data) ? buflen : end - data; memcpy(conn->write_buf + conn->write_pos, data, copylen); conn->write_pos += copylen; return data + copylen;}/* Write data to socket or output file as appropriate. */static svn_error_t *writebuf_output(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *data, apr_size_t len){ const char *end = data + len; apr_status_t status; apr_size_t count; apr_pool_t *subpool = NULL; while (data < end) { count = end - data; if (conn->sock) status = apr_socket_send(conn->sock, data, &count); else status = apr_file_write(conn->out_file, data, &count); if (status) return svn_error_wrap_apr(status, _("Can't write to connection")); if (count == 0) { if (!subpool) subpool = svn_pool_create(pool); else apr_pool_clear(subpool); SVN_ERR(conn->block_handler(conn, subpool, conn->block_baton)); } data += count; } if (subpool) apr_pool_destroy(subpool); return SVN_NO_ERROR;}/* Write data from the write buffer out to the socket. */static svn_error_t *writebuf_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool){ int write_pos = conn->write_pos; /* Clear conn->write_pos first in case the block handler does a read. */ conn->write_pos = 0; SVN_ERR(writebuf_output(conn, pool, conn->write_buf, write_pos)); return SVN_NO_ERROR;}static svn_error_t *writebuf_write(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *data, apr_size_t len){ const char *end = data + len; if (conn->write_pos > 0 && conn->write_pos + len > sizeof(conn->write_buf)) { /* Fill and then empty the write buffer. */ data = writebuf_push(conn, data, end); SVN_ERR(writebuf_flush(conn, pool)); } if (end - data > (apr_ssize_t)sizeof(conn->write_buf)) SVN_ERR(writebuf_output(conn, pool, data, end - data)); else writebuf_push(conn, data, end); return SVN_NO_ERROR;}static svn_error_t *writebuf_printf(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *fmt, ...) __attribute__ ((format(printf, 3, 4)));static svn_error_t *writebuf_printf(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *fmt, ...){ va_list ap; char *str; va_start(ap, fmt); str = apr_pvsprintf(pool, fmt, ap); va_end(ap); return writebuf_write(conn, pool, str, strlen(str));}/* --- READ BUFFER MANAGEMENT --- *//* Read bytes into DATA until either the read buffer is empty or * we reach END. */static char *readbuf_drain(svn_ra_svn_conn_t *conn, char *data, char *end){ apr_ssize_t buflen, copylen; buflen = conn->read_end - conn->read_ptr; copylen = (buflen < end - data) ? buflen : end - data; memcpy(data, conn->read_ptr, copylen); conn->read_ptr += copylen; return data + copylen;}/* Read data from socket or input file as appropriate. */static svn_error_t *readbuf_input(svn_ra_svn_conn_t *conn, char *data, apr_size_t *len){ apr_status_t status; /* Always block for reading. */ if (conn->sock && conn->block_handler) apr_socket_timeout_set(conn->sock, -1); if (conn->sock) status = apr_socket_recv(conn->sock, data, len); else status = apr_file_read(conn->in_file, data, len); if (conn->sock && conn->block_handler) apr_socket_timeout_set(conn->sock, 0); if (status && !APR_STATUS_IS_EOF(status)) return svn_error_wrap_apr(status, _("Can't read from connection")); if (*len == 0) return svn_error_create(SVN_ERR_RA_SVN_CONNECTION_CLOSED, NULL, _("Connection closed unexpectedly")); return SVN_NO_ERROR;}/* Read data from the socket into the read buffer, which must be empty. */static svn_error_t *readbuf_fill(svn_ra_svn_conn_t *conn, apr_pool_t *pool){ apr_size_t len; assert(conn->read_ptr == conn->read_end); SVN_ERR(writebuf_flush(conn, pool)); len = sizeof(conn->read_buf); SVN_ERR(readbuf_input(conn, conn->read_buf, &len)); conn->read_ptr = conn->read_buf; conn->read_end = conn->read_buf + len; return SVN_NO_ERROR;}static svn_error_t *readbuf_getchar(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result){ if (conn->read_ptr == conn->read_end) SVN_ERR(readbuf_fill(conn, pool)); *result = *conn->read_ptr++; return SVN_NO_ERROR;}static svn_error_t *readbuf_getchar_skip_whitespace(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *result){ do SVN_ERR(readbuf_getchar(conn, pool, result)); while (svn_iswhitespace(*result)); return SVN_NO_ERROR;}static svn_error_t *readbuf_read(svn_ra_svn_conn_t *conn, apr_pool_t *pool, char *data, apr_size_t len){ char *end = data + len; apr_size_t count; /* Copy in an appropriate amount of data from the buffer. */ data = readbuf_drain(conn, data, end); /* Read large chunks directly into buffer. */ while (end - data > (apr_ssize_t)sizeof(conn->read_buf)) { SVN_ERR(writebuf_flush(conn, pool)); count = end - data; SVN_ERR(readbuf_input(conn, data, &count)); data += count; } while (end > data) { /* The remaining amount to read is small; fill the buffer and * copy from that. */ SVN_ERR(readbuf_fill(conn, pool)); data = readbuf_drain(conn, data, end); } return SVN_NO_ERROR;}static svn_error_t *readbuf_skip_leading_garbage(svn_ra_svn_conn_t *conn){ char buf[256]; /* Must be smaller than sizeof(conn->read_buf) - 1. */ const char *p, *end; apr_size_t len; svn_boolean_t lparen = FALSE; assert(conn->read_ptr == conn->read_end); while (1) { /* Read some data directly from the connection input source. */ len = sizeof(buf); SVN_ERR(readbuf_input(conn, buf, &len)); end = buf + len; /* Scan the data for '(' WS with a very simple state machine. */ for (p = buf; p < end; p++) { if (lparen && svn_iswhitespace(*p)) break; else lparen = (*p == '('); } if (p < end) break; } /* p now points to the whitespace just after the left paren. Fake * up the left paren and then copy what we have into the read * buffer. */ conn->read_buf[0] = '('; memcpy(conn->read_buf + 1, p, end - p); conn->read_ptr = conn->read_buf; conn->read_end = conn->read_buf + 1 + (end - p); return SVN_NO_ERROR;}/* --- WRITING DATA ITEMS --- */ svn_error_t *svn_ra_svn_write_number(svn_ra_svn_conn_t *conn, apr_pool_t *pool, apr_uint64_t number){ return writebuf_printf(conn, pool, "%" APR_UINT64_T_FMT " ", number);}svn_error_t *svn_ra_svn_write_string(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const svn_string_t *str){ SVN_ERR(writebuf_printf(conn, pool, "%" APR_SIZE_T_FMT ":", str->len)); SVN_ERR(writebuf_write(conn, pool, str->data, str->len)); SVN_ERR(writebuf_write(conn, pool, " ", 1)); return SVN_NO_ERROR;}svn_error_t *svn_ra_svn_write_cstring(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *s){ return writebuf_printf(conn, pool, "%" APR_SIZE_T_FMT ":%s ", strlen(s), s);}svn_error_t *svn_ra_svn_write_word(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *word){ return writebuf_printf(conn, pool, "%s ", word);}svn_error_t *svn_ra_svn_start_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool){ return writebuf_write(conn, pool, "( ", 2);}svn_error_t *svn_ra_svn_end_list(svn_ra_svn_conn_t *conn, apr_pool_t *pool){ return writebuf_write(conn, pool, ") ", 2);}svn_error_t *svn_ra_svn_flush(svn_ra_svn_conn_t *conn, apr_pool_t *pool){ return writebuf_flush(conn, pool);}/* --- WRITING TUPLES --- */static svn_error_t *vwrite_tuple(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const char *fmt, va_list ap){ svn_boolean_t opt = FALSE; svn_revnum_t rev; const char *cstr; const svn_string_t *str; if (*fmt == '!') fmt++; else SVN_ERR(svn_ra_svn_start_list(conn, pool)); for (; *fmt; fmt++) { if (*fmt == 'n' && !opt) SVN_ERR(svn_ra_svn_write_number(conn, pool, va_arg(ap, apr_uint64_t))); else if (*fmt == 'r') { rev = va_arg(ap, svn_revnum_t); assert(opt || SVN_IS_VALID_REVNUM(rev)); if (SVN_IS_VALID_REVNUM(rev)) SVN_ERR(svn_ra_svn_write_number(conn, pool, rev)); } else if (*fmt == 's') { str = va_arg(ap, const svn_string_t *); assert(opt || str); if (str) SVN_ERR(svn_ra_svn_write_string(conn, pool, str)); } else if (*fmt == 'c') { cstr = va_arg(ap, const char *); assert(opt || cstr); if (cstr) SVN_ERR(svn_ra_svn_write_cstring(conn, pool, cstr)); } else if (*fmt == 'w') { cstr = va_arg(ap, const char *); assert(opt || cstr); if (cstr) SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr)); } else if (*fmt == 'b' && !opt) { cstr = va_arg(ap, svn_boolean_t) ? "true" : "false"; SVN_ERR(svn_ra_svn_write_word(conn, pool, cstr)); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -