marshal.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 882 行 · 第 1/2 页
C
882 行
/*
* 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 "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);
}
void svn_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;
return ((apr_poll(&pfd, 1, &n, 0) == APR_SUCCESS) && n);
}
/* --- 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, ...)
{
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);
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))
{
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);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?