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 + -
显示快捷键?