text:
#include <stdlib.h>

#include <apr_file_io.h>

#include "svn_private_config.h"
#include "svn_cmdline.h"
#include "svn_error.h"
#include "svn_string.h"
#include "svn_opt.h"
#include "svn_utf.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_hash.h"
#include "svn_repos.h"
#include "svn_fs.h"
#include "svn_pools.h"
#include "svn_sorts.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_version.h"

#include "private/svn_repos_private.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_sorts_private.h"

/*** Code. ***/

/* Writes a property in dumpfile format to given stringbuf. */
static void
write_prop_to_stringbuf(svn_stringbuf_t *strbuf,
                        const char *name,
                        const svn_string_t *value)
{
  int bytes_used;
  size_t namelen;
  char buf[SVN_KEYLINE_MAXLEN];

  /* Output name length, then name. */
  namelen = strlen(name);
  svn_stringbuf_appendbytes(strbuf, "K ", 2);

  bytes_used = apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, namelen);
  svn_stringbuf_appendbytes(strbuf, buf, bytes_used);
  svn_stringbuf_appendbyte(strbuf, '\n');

  svn_stringbuf_appendbytes(strbuf, name, namelen);
  svn_stringbuf_appendbyte(strbuf, '\n');

  /* Output value length, then value. */
  svn_stringbuf_appendbytes(strbuf, "V ", 2);

  bytes_used = apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, value->len);
  svn_stringbuf_appendbytes(strbuf, buf, bytes_used);
  svn_stringbuf_appendbyte(strbuf, '\n');

  svn_stringbuf_appendbytes(strbuf, value->data, value->len);
  svn_stringbuf_appendbyte(strbuf, '\n');
}

/* Writes a property deletion in dumpfile format to given stringbuf. */
static void
write_propdel_to_stringbuf(svn_stringbuf_t **strbuf,
                           const char *name)
{
  int bytes_used;
  size_t namelen;
  char buf[SVN_KEYLINE_MAXLEN];

  /* Output name length, then name. */
  namelen = strlen(name);
  svn_stringbuf_appendbytes(*strbuf, "D ", 2);

  bytes_used = apr_snprintf(buf, sizeof(buf), "%" APR_SIZE_T_FMT, namelen);
  svn_stringbuf_appendbytes(*strbuf, buf, bytes_used);
  svn_stringbuf_appendbyte(*strbuf, '\n');

  svn_stringbuf_appendbytes(*strbuf, name, namelen);
  svn_stringbuf_appendbyte(*strbuf, '\n');
}

/* Compare the node-path PATH with the (const char *) prefixes in PFXLIST.
 * Return TRUE if any prefix is a prefix of PATH (matching whole path
 * components); FALSE otherwise.
 * PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
static svn_boolean_t
ary_prefix_match(const apr_array_header_t *pfxlist, const char *path)
{
  int i;
  size_t path_len = strlen(path);

  for (i = 0; i < pfxlist->nelts; i++)
    {
      const char *pfx = APR_ARRAY_IDX(pfxlist, i, const char *);
      size_t pfx_len = strlen(pfx);

      if (path_len < pfx_len)
        continue;
      if (strncmp(path, pfx, pfx_len) == 0
          && (pfx_len == 1 || path[pfx_len] == '\0' || path[pfx_len] == '/'))
        return TRUE;
    }

  return FALSE;
}

/* Check whether we need to skip this PATH based on its presence in
   the PREFIXES list, and the DO_EXCLUDE option.
   PATH starts with a '/', as do the (const char *) paths in PREFIXES. */
static APR_INLINE svn_boolean_t
skip_path(const char *path, const apr_array_header_t *prefixes,
          svn_boolean_t do_exclude, svn_boolean_t glob)
{
  const svn_boolean_t matches =
    (glob
     ? svn_cstring_match_glob_list(path, prefixes)
     : ary_prefix_match(prefixes, path));

  /* NXOR */
  return (matches ? do_exclude : !do_exclude);
}

/* Note: the input stream parser calls us with events.
   Output of the filtered dump occurs for the most part streamily with the
   event callbacks, to avoid caching large quantities of data in memory.
   The exceptions this are:
   - All revision data (headers and props) must be cached until a non-skipped
     node within the revision is found, or the revision is closed.
   - Node headers and props must be cached until all props have been received
     (to allow the Prop-content-length to be found). This is signalled either
     by the node text arriving, or the node being closed.
   The writing_begun members of the associated object batons track the state.
   output_revision() and output_node() are called to cause this flushing of
   cached data to occur.
*/

/* Filtering batons */

struct revmap_t
{
  svn_revnum_t rev; /* Last non-dropped revision to which this maps. */
  svn_boolean_t was_dropped; /* Was this revision dropped? */
};

struct parse_baton_t
{
  /* Command-line options values. */
  svn_boolean_t do_exclude;
  svn_boolean_t quiet;
  svn_boolean_t glob;
  svn_boolean_t drop_empty_revs;
  svn_boolean_t drop_all_empty_revs;
  svn_boolean_t do_renumber_revs;
  svn_boolean_t preserve_revprops;
  svn_boolean_t skip_missing_merge_sources;
  svn_boolean_t allow_deltas;
  apr_array_header_t *prefixes;

  /* Input and output streams. */
  svn_stream_t *in_stream;
  svn_stream_t *out_stream;

  /* State for the filtering process. */
  apr_int32_t rev_drop_count;
  apr_hash_t *dropped_nodes;
  apr_hash_t *renumber_history;  /* svn_revnum_t -> struct revmap_t */
  svn_revnum_t last_live_revision;
  /* The oldest original revision, greater than r0, in the input
     stream which was not filtered. */
  svn_revnum_t oldest_original_rev;
};

struct revision_baton_t
{
  /* Reference to the global parse baton. */
  struct parse_baton_t *pb;

  /* Does this revision have node or prop changes? */
  svn_boolean_t has_nodes;

  /* Did we drop any nodes? */
  svn_boolean_t had_dropped_nodes;

  /* Written to output stream? */
  svn_boolean_t writing_begun;

  /* The original and new (re-mapped) revision numbers. */
  svn_revnum_t rev_orig;
  svn_revnum_t rev_actual;

  /* Pointers to dumpfile data. */
  apr_hash_t *original_headers;
  apr_hash_t *props;
};

struct node_baton_t
{
  /* Reference to the current revision baton. */
  struct revision_baton_t *rb;

  /* Are we skipping this node? */
  svn_boolean_t do_skip;

  /* Have we been instructed to change or remove props on, or change
     the text of, this node? */
  svn_boolean_t has_props;
  svn_boolean_t has_text;

  /* Written to output stream? */
  svn_boolean_t writing_begun;

  /* The text content length according to the dumpfile headers, because we
     need the length before we have the actual text. */
  svn_filesize_t tcl;

  /* Pointers to dumpfile data. */
  svn_repos__dumpfile_headers_t *headers;
  svn_stringbuf_t *props;

  /* Expect deltas? */
  svn_boolean_t has_prop_delta;
  svn_boolean_t has_text_delta;

  /* We might need the node path in a parse error message. */
  char *node_path;

  apr_pool_t *node_pool;
};

/* Filtering vtable members */

/* File-format stamp. */
static svn_error_t *
magic_header_record(int version, void *parse_baton, apr_pool_t *pool)
{
  struct parse_baton_t *pb = parse_baton;

  if (version >= SVN_REPOS_DUMPFILE_FORMAT_VERSION_DELTAS)
    pb->allow_deltas = TRUE;

  SVN_ERR(svn_stream_printf(pb->out_stream, pool,
                            SVN_REPOS_DUMPFILE_MAGIC_HEADER ": %d\n\n",
                            version));

  return SVN_NO_ERROR;
}

/* Return a deep copy of a (char * -> char *) hash. */
static apr_hash_t *
headers_dup(apr_hash_t *headers,
            apr_pool_t *pool)
{
  apr_hash_t *new_hash = apr_hash_make(pool);
  apr_hash_index_t *hi;

  for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
    {
      const char *key = apr_hash_this_key(hi);
      const char *val = apr_hash_this_val(hi);

      svn_hash_sets(new_hash, apr_pstrdup(pool, key), apr_pstrdup(pool, val));
    }
  return new_hash;
}

/* New revision: set up revision_baton, decide if we skip it. */
static svn_error_t *
new_revision_record(void **revision_baton,
                    apr_hash_t *headers,
                    void *parse_baton,
                    apr_pool_t *pool)
{
  struct revision_baton_t *rb;
  const char *rev_orig;

  *revision_baton = apr_palloc(pool, sizeof(struct revision_baton_t));
  rb = *revision_baton;
  rb->pb = parse_baton;
  rb->has_nodes = FALSE;
  rb->had_dropped_nodes = FALSE;
  rb->writing_begun = FALSE;
  rb->props = apr_hash_make(pool);
  rb->original_headers = headers_dup(headers, pool);

  rev_orig = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_REVISION_NUMBER);
  rb->rev_orig = SVN_STR_TO_REV(rev_orig);

  if (rb->pb->do_renumber_revs)
    rb->rev_actual = rb->rev_orig - rb->pb->rev_drop_count;
  else
    rb->rev_actual = rb->rev_orig;

  return SVN_NO_ERROR;
}

/* Output revision to dumpstream
   This may be called by new_node_record(), iff rb->has_nodes has been set
   to TRUE, or by close_revision() otherwise. This must only be called
   if rb->writing_begun is FALSE. */
static svn_error_t *
output_revision(struct revision_baton_t *rb)
{
  svn_boolean_t write_out_rev = FALSE;
  apr_pool_t *hash_pool = apr_hash_pool_get(rb->props);
  apr_pool_t *subpool = svn_pool_create(hash_pool);

  rb->writing_begun = TRUE;

  /* If this revision has no nodes left because the ones it had were
     dropped, and we are not dropping empty revisions, and we were not
     told to preserve revision props, then we want to fixup the
     revision props to only contain:
       - the date
       - a log message that reports that this revision is just stuffing. */
  if ((! rb->pb->preserve_revprops)
      && (! rb->has_nodes)
      && rb->had_dropped_nodes
      && (! rb->pb->drop_empty_revs)
      && (! rb->pb->drop_all_empty_revs))
    {
      apr_hash_t *old_props = rb->props;
      rb->props = apr_hash_make(hash_pool);
      svn_hash_sets(rb->props, SVN_PROP_REVISION_DATE,
                    svn_hash_gets(old_props, SVN_PROP_REVISION_DATE));
      svn_hash_sets(rb->props, SVN_PROP_REVISION_LOG,
                    svn_string_create(_("This is an empty revision for "
                                        "padding."), hash_pool));
    }

  /* write out the revision */
  /* Revision is written out in the following cases:
     1. If the revision has nodes or
     it is revision 0 (Special case: To preserve the props on r0).
     2. --drop-empty-revs has been supplied,
     but revision has not all nodes dropped.
     3. If no --drop-empty-revs or --drop-all-empty-revs have been supplied,
     write out the revision which has no nodes to begin with.
  */
  if (rb->has_nodes || (rb->rev_orig == 0))
    write_out_rev = TRUE;
  else if (rb->pb->drop_empty_revs)
    write_out_rev = ! rb->had_dropped_nodes;
  else if (! rb->pb->drop_all_empty_revs)
    write_out_rev = TRUE;

  if (write_out_rev)
    {
      /* This revision is a keeper. */
      SVN_ERR(svn_repos__dump_revision_record(rb->pb->out_stream,
                                              rb->rev_actual,
                                              rb->original_headers,
                                              rb->props,
                                              FALSE /*props_section_always*/,
                                              subpool));

      /* Stash the oldest original rev not dropped. */
      if (rb->rev_orig > 0
          && !SVN_IS_VALID_REVNUM(rb->pb->oldest_original_rev))
        rb->pb->oldest_original_rev = rb->rev_orig;

      if (rb->pb->do_renumber_revs)
        {
          svn_revnum_t *rr_key;
          struct revmap_t *rr_val;
          apr_pool_t *rr_pool = apr_hash_pool_get(rb->pb->renumber_history);
          rr_key = apr_palloc(rr_pool, sizeof(*rr_key));
          rr_val = apr_palloc(rr_pool, sizeof(*rr_val));
          *rr_key = rb->rev_orig;
          rr_val->rev = rb->rev_actual;
          rr_val->was_dropped = FALSE;
          apr_hash_set(rb->pb->renumber_history, rr_key,
                       sizeof(*rr_key), rr_val);
          rb->pb->last_live_revision = rb->rev_actual;
        }

      if (! rb->pb->quiet)
        SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                    _("Revision %ld committed as %ld.\n"),
                                    rb->rev_orig, rb->rev_actual));
    }
  else
    {
      /* We're dropping this revision. */
      rb->pb->rev_drop_count++;
      if (rb->pb->do_renumber_revs)
        {
          svn_revnum_t *rr_key;
          struct revmap_t *rr_val;
          apr_pool_t *rr_pool = apr_hash_pool_get(rb->pb->renumber_history);
          rr_key = apr_palloc(rr_pool, sizeof(*rr_key));
          rr_val = apr_palloc(rr_pool, sizeof(*rr_val));
          *rr_key = rb->rev_orig;
          rr_val->rev = rb->pb->last_live_revision;
          rr_val->was_dropped = TRUE;
          apr_hash_set(rb->pb->renumber_history, rr_key,
                       sizeof(*rr_key), rr_val);
        }

      if (! rb->pb->quiet)
        SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                    _("Revision %ld skipped.\n"),
                                    rb->rev_orig));
    }
  svn_pool_destroy(subpool);
  return SVN_NO_ERROR;
}

/* UUID record here: dump it, as we do not filter them. */
static svn_error_t *
uuid_record(const char *uuid, void *parse_baton, apr_pool_t *pool)
{
  struct parse_baton_t *pb = parse_baton;
  SVN_ERR(svn_stream_printf(pb->out_stream, pool,
                            SVN_REPOS_DUMPFILE_UUID ": %s\n\n", uuid));
  return SVN_NO_ERROR;
}

/* New node here. Set up node_baton by copying headers. */
static svn_error_t *
new_node_record(void **node_baton,
                apr_hash_t *headers,
                void *rev_baton,
                apr_pool_t *pool)
{
  struct parse_baton_t *pb;
  struct node_baton_t *nb;
  char *node_path, *copyfrom_path;
  apr_hash_index_t *hi;
  const char *tcl;

  *node_baton = apr_palloc(pool, sizeof(struct node_baton_t));
  nb          = *node_baton;
  nb->rb      = rev_baton;
  nb->node_pool = pool;
  pb          = nb->rb->pb;

  node_path = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_PATH);
  copyfrom_path = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH);

  /* Ensure that paths start with a leading '/'. */
  if (node_path[0] != '/')
    node_path = apr_pstrcat(pool, "/", node_path, SVN_VA_NULL);
  if (copyfrom_path && copyfrom_path[0] != '/')
    copyfrom_path = apr_pstrcat(pool, "/", copyfrom_path, SVN_VA_NULL);

  nb->do_skip = skip_path(node_path, pb->prefixes,
                          pb->do_exclude, pb->glob);

  /* If we're skipping the node, take note of path, discarding the
     rest.  */
  if (nb->do_skip)
    {
      svn_hash_sets(pb->dropped_nodes,
                    apr_pstrdup(apr_hash_pool_get(pb->dropped_nodes),
                                node_path),
                    (void *)1);
      nb->rb->had_dropped_nodes = TRUE;
    }
  else
    {
      const char *kind;
      const char *action;

      tcl = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH);

      /* Test if this node was copied from dropped source. */
      if (copyfrom_path &&
          skip_path(copyfrom_path, pb->prefixes, pb->do_exclude, pb->glob))
        {
          /* This node was copied from a dropped source.
             We have a problem, since we did not want to drop this node too.

             However, there is one special case we'll handle.  If the node is
             a file, and this was a copy-and-modify operation, then the
             dumpfile should contain the new contents of the file.  In this
             scenario, we'll just do an add without history using the new
             contents.  */
          kind = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND);

          /* If there is a Text-content-length header, and the kind is
             "file", we just fallback to an add without history. */
          if (tcl && (strcmp(kind, "file") == 0))
            {
              svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_PATH,
                            NULL);
              svn_hash_sets(headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV,
                            NULL);
              copyfrom_path = NULL;
            }
          /* Else, this is either a directory or a file whose contents we
             don't have readily available.  */
          else
            {
              return svn_error_createf
                (SVN_ERR_INCOMPLETE_DATA, 0,
                 _("Invalid copy source path '%s'"), copyfrom_path);
            }
        }

      nb->has_props = FALSE;
      nb->has_text = FALSE;
      nb->has_prop_delta = FALSE;
      nb->has_text_delta = FALSE;
      nb->writing_begun = FALSE;
      nb->tcl = tcl ? svn__atoui64(tcl) : 0;
      nb->headers = svn_repos__dumpfile_headers_create(pool);
      nb->props = svn_stringbuf_create_empty(pool);
      nb->node_path = apr_pstrdup(pool, node_path);

      /* Now we know for sure that we have a node that will not be
         skipped, flush the revision if it has not already been done. */
      nb->rb->has_nodes = TRUE;
      if (! nb->rb->writing_begun)
        SVN_ERR(output_revision(nb->rb));

      /* A node record is required to begin with 'Node-path', skip the
         leading '/' to match the form used by 'svnadmin dump'. */
      svn_repos__dumpfile_header_push(
        nb->headers, SVN_REPOS_DUMPFILE_NODE_PATH, node_path + 1);

      /* Node-kind is next and is optional. */
      kind = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_KIND);
      if (kind)
        svn_repos__dumpfile_header_push(
          nb->headers, SVN_REPOS_DUMPFILE_NODE_KIND, kind);

      /* Node-action is next and required. */
      action = svn_hash_gets(headers, SVN_REPOS_DUMPFILE_NODE_ACTION);
      if (action)
        svn_repos__dumpfile_header_push(
          nb->headers, SVN_REPOS_DUMPFILE_NODE_ACTION, action);
      else
        return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0,
                                 _("Missing Node-action for path '%s'"),
                                 node_path);

      for (hi = apr_hash_first(pool, headers); hi; hi = apr_hash_next(hi))
        {
          const char *key = apr_hash_this_key(hi);
          const char *val = apr_hash_this_val(hi);

          if ((!strcmp(key, SVN_REPOS_DUMPFILE_PROP_DELTA))
              && (!strcmp(val, "true")))
            nb->has_prop_delta = TRUE;

          if ((!strcmp(key, SVN_REPOS_DUMPFILE_TEXT_DELTA))
              && (!strcmp(val, "true")))
            nb->has_text_delta = TRUE;

          if ((!strcmp(key, SVN_REPOS_DUMPFILE_CONTENT_LENGTH))
              || (!strcmp(key, SVN_REPOS_DUMPFILE_PROP_CONTENT_LENGTH))
              || (!strcmp(key, SVN_REPOS_DUMPFILE_TEXT_CONTENT_LENGTH))
              || (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_PATH))
              || (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_KIND))
              || (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_ACTION)))
            continue;

          /* Rewrite Node-Copyfrom-Rev if we are renumbering revisions.
             The number points to some revision in the past. We keep track
             of revision renumbering in an apr_hash, which maps original
             revisions to new ones. Dropped revision are mapped to -1.
             This should never happen here.
          */
          if (pb->do_renumber_revs
              && (!strcmp(key, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV)))
            {
              svn_revnum_t cf_orig_rev;
              struct revmap_t *cf_renum_val;

              cf_orig_rev = SVN_STR_TO_REV(val);
              cf_renum_val = apr_hash_get(pb->renumber_history,
                                          &cf_orig_rev,
                                          sizeof(cf_orig_rev));
              if (! (cf_renum_val && SVN_IS_VALID_REVNUM(cf_renum_val->rev)))
                return svn_error_createf
                  (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                   _("No valid copyfrom revision in filtered stream"));
              svn_repos__dumpfile_header_pushf(
                nb->headers, SVN_REPOS_DUMPFILE_NODE_COPYFROM_REV,
                "%ld", cf_renum_val->rev);
              continue;
            }

          /* passthru: put header straight to output */
          svn_repos__dumpfile_header_push(nb->headers, key, val);
        }
    }

  return SVN_NO_ERROR;
}

/* Examine the mergeinfo in INITIAL_VAL, omitting missing merge
   sources or renumbering revisions in rangelists as appropriate, and
   return the (possibly new) mergeinfo in *FINAL_VAL (allocated from
   POOL). */
static svn_error_t *
adjust_mergeinfo(svn_string_t **final_val, const svn_string_t *initial_val,
                 struct revision_baton_t *rb, apr_pool_t *pool)
{
  apr_hash_t *mergeinfo;
  apr_hash_t *final_mergeinfo = apr_hash_make(pool);
  apr_hash_index_t *hi;
  apr_pool_t *subpool = svn_pool_create(pool);

  SVN_ERR(svn_mergeinfo_parse(&mergeinfo, initial_val->data, subpool));

  /* Issue #3020: If we are skipping missing merge sources, then also
     filter mergeinfo ranges as old or older than the oldest revision in the
     dump stream.  Those older than the oldest obviously refer to history
     outside of the dump stream.  The oldest rev itself is present in the
     dump, but cannot be a valid merge source revision since it is the
     start of all history.  E.g. if we dump -r100:400 then dumpfilter the
     result with --skip-missing-merge-sources, any mergeinfo with revision
     100 implies a change of -r99:100, but r99 is part of the history we
     want filtered.

     If the oldest rev is r0 then there is nothing to filter. */

  /* ### This seems to cater only for use cases where the revisions being
         processed are not following on from revisions that will already
         exist in the destination repository. If the revisions being
         processed do follow on, then we might want to keep the mergeinfo
         that refers to those older revisions. */

  if (rb->pb->skip_missing_merge_sources && rb->pb->oldest_original_rev > 0)
    SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
      &mergeinfo, mergeinfo,
      rb->pb->oldest_original_rev, 0,
      FALSE, subpool, subpool));

  for (hi = apr_hash_first(subpool, mergeinfo); hi; hi = apr_hash_next(hi))
    {
      const char *merge_source = apr_hash_this_key(hi);
      svn_rangelist_t *rangelist = apr_hash_this_val(hi);
      struct parse_baton_t *pb = rb->pb;

      /* Determine whether the merge_source is a part of the prefix. */
      if (skip_path(merge_source, pb->prefixes, pb->do_exclude, pb->glob))
        {
          if (pb->skip_missing_merge_sources)
            continue;
          else
            return svn_error_createf(SVN_ERR_INCOMPLETE_DATA, 0,
                                     _("Missing merge source path '%s'; try "
                                       "with --skip-missing-merge-sources"),
                                     merge_source);
        }

      /* Possibly renumber revisions in merge source's rangelist. */
      if (pb->do_renumber_revs)
        {
          int i;

          for (i = 0; i < rangelist->nelts; i++)
            {
              struct revmap_t *revmap_start;
              struct revmap_t *revmap_end;
              svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, i,
                                                       svn_merge_range_t *);

              revmap_start = apr_hash_get(pb->renumber_history,
                                          &range->start, sizeof(range->start));
              if (! (revmap_start && SVN_IS_VALID_REVNUM(revmap_start->rev)))
                return svn_error_createf
                  (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                   _("No valid revision range 'start' in filtered stream"));

              revmap_end = apr_hash_get(pb->renumber_history,
                                        &range->end, sizeof(range->end));
              if (! (revmap_end && SVN_IS_VALID_REVNUM(revmap_end->rev)))
                return svn_error_createf
                  (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
                   _("No valid revision range 'end' in filtered stream"));

              range->start = revmap_start->rev;
              range->end = revmap_end->rev;
            }
        }
      svn_hash_sets(final_mergeinfo, merge_source, rangelist);
    }

  SVN_ERR(svn_mergeinfo__canonicalize_ranges(final_mergeinfo, subpool));
  SVN_ERR(svn_mergeinfo_to_string(final_val, final_mergeinfo, pool));
  svn_pool_destroy(subpool);

  return SVN_NO_ERROR;
}

static svn_error_t *
set_revision_property(void *revision_baton,
                      const char *name,
                      const svn_string_t *value)
{
  struct revision_baton_t *rb = revision_baton;
  apr_pool_t *hash_pool = apr_hash_pool_get(rb->props);

  svn_hash_sets(rb->props,
                apr_pstrdup(hash_pool, name),
                svn_string_dup(value, hash_pool));
  return SVN_NO_ERROR;
}

static svn_error_t *
set_node_property(void *node_baton,
                  const char *name,
                  const svn_string_t *value)
{
  struct node_baton_t *nb = node_baton;
  struct revision_baton_t *rb = nb->rb;

  if (nb->do_skip)
    return SVN_NO_ERROR;

  /* Try to detect if a delta-mode property occurs unexpectedly. HAS_PROPS
     can be false here only if the parser didn't call remove_node_props(),
     so this may indicate a bug rather than bad data. */
  if (! (nb->has_props || nb->has_prop_delta))
    return svn_error_createf(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
                             _("Delta property block detected, but deltas "
                               "are not enabled for node '%s' in original "
                               "revision %ld"),
                             nb->node_path, rb->rev_orig);

  if (strcmp(name, SVN_PROP_MERGEINFO) == 0)
    {
      svn_string_t *filtered_mergeinfo;  /* Avoid compiler warning. */
      apr_pool_t *pool = apr_hash_pool_get(rb->props);
      SVN_ERR(adjust_mergeinfo(&filtered_mergeinfo, value, rb, pool));
      value = filtered_mergeinfo;
    }

  nb->has_props = TRUE;
  write_prop_to_stringbuf(nb->props, name, value);

  return SVN_NO_ERROR;
}

static svn_error_t *
delete_node_property(void *node_baton, const char *name)
{
  struct node_baton_t *nb = node_baton;
  struct revision_baton_t *rb = nb->rb;

  if (nb->do_skip)
    return SVN_NO_ERROR;

  if (!nb->has_prop_delta)
    return svn_error_createf(SVN_ERR_STREAM_MALFORMED_DATA, NULL,
                             _("Delta property block detected, but deltas "
                               "are not enabled for node '%s' in original "
                               "revision %ld"),
                             nb->node_path, rb->rev_orig);

  nb->has_props = TRUE;
  write_propdel_to_stringbuf(&(nb->props), name);

  return SVN_NO_ERROR;
}

/* The parser calls this method if the node record has a non-delta
 * property content section, before any calls to set_node_property().
 * If the node record uses property deltas, this is not called.
 */
static svn_error_t *
remove_node_props(void *node_baton)
{
  struct node_baton_t *nb = node_baton;

  /* In this case, not actually indicating that the node *has* props,
     rather that it has a property content section. */
  nb->has_props = TRUE;

  return SVN_NO_ERROR;
}

static svn_error_t *
set_fulltext(svn_stream_t **stream, void *node_baton)
{
  struct node_baton_t *nb = node_baton;

  if (!nb->do_skip)
    {
      nb->has_text = TRUE;
      if (! nb->writing_begun)
        {
          nb->writing_begun = TRUE;
          if (nb->has_props)
            {
              svn_stringbuf_appendcstr(nb->props, "PROPS-END\n");
            }
          SVN_ERR(svn_repos__dump_node_record(nb->rb->pb->out_stream,
                                              nb->headers,
                                              nb->has_props ? nb->props : NULL,
                                              nb->has_text,
                                              nb->tcl,
                                              TRUE /*content_length_always*/,
                                              nb->node_pool));
        }
      *stream = nb->rb->pb->out_stream;
    }

  return SVN_NO_ERROR;
}

/* Finalize node */
static svn_error_t *
close_node(void *node_baton)
{
  struct node_baton_t *nb = node_baton;
  apr_size_t len = 2;

  /* Get out of here if we can. */
  if (nb->do_skip)
    return SVN_NO_ERROR;

  /* If the node was not flushed already to output its text, do it now. */
  if (! nb->writing_begun)
    {
      nb->writing_begun = TRUE;
      if (nb->has_props)
        {
          svn_stringbuf_appendcstr(nb->props, "PROPS-END\n");
        }
      SVN_ERR(svn_repos__dump_node_record(nb->rb->pb->out_stream,
                                          nb->headers,
                                          nb->has_props ? nb->props : NULL,
                                          nb->has_text,
                                          nb->tcl,
                                          TRUE /*content_length_always*/,
                                          nb->node_pool));
    }

  /* put an end to node. */
  SVN_ERR(svn_stream_write(nb->rb->pb->out_stream, "\n\n", &len));

  return SVN_NO_ERROR;
}

/* Finalize revision */
static svn_error_t *
close_revision(void *revision_baton)
{
  struct revision_baton_t *rb = revision_baton;

  /* If no node has yet flushed the revision, do it now. */
  if (! rb->writing_begun)
    return output_revision(rb);
  else
    return SVN_NO_ERROR;
}

/* Filtering vtable */
static svn_repos_parse_fns3_t filtering_vtable =
  {
    magic_header_record,
    uuid_record,
    new_revision_record,
    new_node_record,
    set_revision_property,
    set_node_property,
    delete_node_property,
    remove_node_props,
    set_fulltext,
    NULL,
    close_node,
    close_revision
  };

/** Subcommands. **/

static svn_opt_subcommand_t
  subcommand_help,
  subcommand_exclude,
  subcommand_include;

enum
  {
    svndumpfilter__drop_empty_revs = SVN_OPT_FIRST_LONGOPT_ID,
    svndumpfilter__drop_all_empty_revs,
    svndumpfilter__renumber_revs,
    svndumpfilter__preserve_revprops,
    svndumpfilter__skip_missing_merge_sources,
    svndumpfilter__targets,
    svndumpfilter__quiet,
    svndumpfilter__glob,
    svndumpfilter__version
  };

/* Option codes and descriptions.
 *
 * The entire list must be terminated with an entry of nulls.
 */
static const apr_getopt_option_t options_table[] =
  {
    {"help",          'h', 0,
     N_("show help on a subcommand")},

    {NULL,            '?', 0,
     N_("show help on a subcommand")},

    {"version",            svndumpfilter__version, 0,
     N_("show program version information") },
    {"quiet",              svndumpfilter__quiet, 0,
     N_("Do not display filtering statistics.") },
    {"pattern",            svndumpfilter__glob, 0,
     N_("Treat the path prefixes as file glob patterns.\n"
        "                             Glob special characters are '*' '?' '[]' and '\\'.\n"
        "                             Character '/' is not treated specially, so\n"
        "                             pattern /*/foo matches paths /a/foo and /a/b/foo.") },
    {"drop-empty-revs",    svndumpfilter__drop_empty_revs, 0,
     N_("Remove revisions emptied by filtering.")},
    {"drop-all-empty-revs",    svndumpfilter__drop_all_empty_revs, 0,
     N_("Remove all empty revisions found in dumpstream\n"
        "                             except revision 0.")},
    {"renumber-revs",      svndumpfilter__renumber_revs, 0,
     N_("Renumber revisions left after filtering.") },
    {"skip-missing-merge-sources",
     svndumpfilter__skip_missing_merge_sources, 0,
     N_("Skip missing merge sources.") },
    {"preserve-revprops",  svndumpfilter__preserve_revprops, 0,
     N_("Don't filter revision properties.") },
    {"targets", svndumpfilter__targets, 1,
     N_("Read additional prefixes, one per line, from\n"
        "                             file ARG.")},
    {NULL}
  };

/* Array of available subcommands.
 * The entire list must be terminated with an entry of nulls.
 */
static const svn_opt_subcommand_desc2_t cmd_table[] =
  {
    {"exclude", subcommand_exclude, {0},
     N_("Filter out nodes with given prefixes from dumpstream.\n"
        "usage: svndumpfilter exclude PATH_PREFIX...\n"),
     {svndumpfilter__drop_empty_revs, svndumpfilter__drop_all_empty_revs,
      svndumpfilter__renumber_revs,
      svndumpfilter__skip_missing_merge_sources, svndumpfilter__targets,
      svndumpfilter__preserve_revprops, svndumpfilter__quiet,
      svndumpfilter__glob} },

    {"include", subcommand_include, {0},
     N_("Filter out nodes without given prefixes from dumpstream.\n"
        "usage: svndumpfilter include PATH_PREFIX...\n"),
     {svndumpfilter__drop_empty_revs, svndumpfilter__drop_all_empty_revs,
      svndumpfilter__renumber_revs,
      svndumpfilter__skip_missing_merge_sources, svndumpfilter__targets,
      svndumpfilter__preserve_revprops, svndumpfilter__quiet,
      svndumpfilter__glob} },

    {"help", subcommand_help, {"?", "h"},
     N_("Describe the usage of this program or its subcommands.\n"
        "usage: svndumpfilter help [SUBCOMMAND...]\n"),
     {0} },

    { NULL, NULL, {0}, NULL, {0} }
  };

/* Baton for passing option/argument state to a subcommand function. */
struct svndumpfilter_opt_state
{
  svn_opt_revision_t start_revision;     /* -r X[:Y] is         */
  svn_opt_revision_t end_revision;       /* not implemented.    */
  svn_boolean_t quiet;                   /* --quiet             */
  svn_boolean_t glob;                    /* --pattern           */
  svn_boolean_t version;                 /* --version           */
  svn_boolean_t drop_empty_revs;         /* --drop-empty-revs   */
  svn_boolean_t drop_all_empty_revs;     /* --drop-all-empty-revs */
  svn_boolean_t help;                    /* --help or -?        */
  svn_boolean_t renumber_revs;           /* --renumber-revs     */
  svn_boolean_t preserve_revprops;       /* --preserve-revprops */
  svn_boolean_t skip_missing_merge_sources;
                                         /* --skip-missing-merge-sources */
  const char *targets_file;              /* --targets-file       */
  apr_array_header_t *prefixes;          /* mainargs.           */
};

static svn_error_t *
parse_baton_initialize(struct parse_baton_t **pb,
                       struct svndumpfilter_opt_state *opt_state,
                       svn_boolean_t do_exclude,
                       apr_pool_t *pool)
{
  struct parse_baton_t *baton = apr_palloc(pool, sizeof(*baton));

  /* Read the stream from STDIN.  Users can redirect a file. */
  SVN_ERR(svn_stream_for_stdin2(&baton->in_stream, TRUE, pool));

  /* Have the parser dump results to STDOUT. Users can redirect a file. */
  SVN_ERR(svn_stream_for_stdout(&baton->out_stream, pool));

  baton->do_exclude = do_exclude;

  /* Ignore --renumber-revs if there can't possibly be
     anything to renumber. */
  baton->do_renumber_revs =
    (opt_state->renumber_revs && (opt_state->drop_empty_revs
                                  || opt_state->drop_all_empty_revs));

  baton->drop_empty_revs = opt_state->drop_empty_revs;
  baton->drop_all_empty_revs = opt_state->drop_all_empty_revs;
  baton->preserve_revprops = opt_state->preserve_revprops;
  baton->quiet = opt_state->quiet;
  baton->glob = opt_state->glob;
  baton->prefixes = opt_state->prefixes;
  baton->skip_missing_merge_sources = opt_state->skip_missing_merge_sources;
  baton->rev_drop_count = 0; /* used to shift revnums while filtering */
  baton->dropped_nodes = apr_hash_make(pool);
  baton->renumber_history = apr_hash_make(pool);
  baton->last_live_revision = SVN_INVALID_REVNUM;
  baton->oldest_original_rev = SVN_INVALID_REVNUM;
  baton->allow_deltas = FALSE;

  *pb = baton;
  return SVN_NO_ERROR;
}

/* This implements `help` subcommand. */
static svn_error_t *
subcommand_help(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
  struct svndumpfilter_opt_state *opt_state = baton;
  const char *header =
    _("general usage: svndumpfilter SUBCOMMAND [ARGS & OPTIONS ...]\n"
      "Subversion repository dump filtering tool.\n"
      "Type 'svndumpfilter help <subcommand>' for help on a "
      "specific subcommand.\n"
      "Type 'svndumpfilter --version' to see the program version.\n"
      "\n"
      "Available subcommands:\n");

  SVN_ERR(svn_opt_print_help4(os, "svndumpfilter",
                              opt_state ? opt_state->version : FALSE,
                              opt_state ? opt_state->quiet : FALSE,
                              /*###opt_state ? opt_state->verbose :*/ FALSE,
                              NULL, header, cmd_table, options_table,
                              NULL, NULL, pool));

  return SVN_NO_ERROR;
}

/* Version compatibility check */
static svn_error_t *
check_lib_versions(void)
{
  static const svn_version_checklist_t checklist[] =
    {
      { "svn_subr",  svn_subr_version },
      { "svn_repos", svn_repos_version },
      { "svn_delta", svn_delta_version },
      { NULL, NULL }
    };
  SVN_VERSION_DEFINE(my_version);

  return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
}

/* Do the real work of filtering. */
static svn_error_t *
do_filter(apr_getopt_t *os,
          void *baton,
          svn_boolean_t do_exclude,
          apr_pool_t *pool)
{
  struct svndumpfilter_opt_state *opt_state = baton;
  struct parse_baton_t *pb;
  apr_hash_index_t *hi;
  apr_array_header_t *keys;
  int i, num_keys;

  if (! opt_state->quiet)
    {
      apr_pool_t *subpool = svn_pool_create(pool);

      if (opt_state->glob)
        {
          SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                      do_exclude
                                      ? (opt_state->drop_empty_revs
                                         || opt_state->drop_all_empty_revs)
                                        ? _("Excluding (and dropping empty "
                                            "revisions for) prefix patterns:\n")
                                        : _("Excluding prefix patterns:\n")
                                      : (opt_state->drop_empty_revs
                                         || opt_state->drop_all_empty_revs)
                                        ? _("Including (and dropping empty "
                                            "revisions for) prefix patterns:\n")
                                        : _("Including prefix patterns:\n")));
        }
      else
        {
          SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                      do_exclude
                                      ? (opt_state->drop_empty_revs
                                         || opt_state->drop_all_empty_revs)
                                        ? _("Excluding (and dropping empty "
                                            "revisions for) prefixes:\n")
                                        : _("Excluding prefixes:\n")
                                      : (opt_state->drop_empty_revs
                                         || opt_state->drop_all_empty_revs)
                                        ? _("Including (and dropping empty "
                                            "revisions for) prefixes:\n")
                                        : _("Including prefixes:\n")));
        }

      for (i = 0; i < opt_state->prefixes->nelts; i++)
        {
          svn_pool_clear(subpool);
          SVN_ERR(svn_cmdline_fprintf
                  (stderr, subpool, "   '%s'\n",
                   APR_ARRAY_IDX(opt_state->prefixes, i, const char *)));
        }

      SVN_ERR(svn_cmdline_fputs("\n", stderr, subpool));
      svn_pool_destroy(subpool);
    }

  SVN_ERR(parse_baton_initialize(&pb, opt_state, do_exclude, pool));
  SVN_ERR(svn_repos_parse_dumpstream3(pb->in_stream, &filtering_vtable, pb,
                                      TRUE, NULL, NULL, pool));

  /* The rest of this is just reporting.  If we aren't reporting, get
     outta here. */
  if (opt_state->quiet)
    return SVN_NO_ERROR;

  SVN_ERR(svn_cmdline_fputs("\n", stderr, pool));

  if (pb->rev_drop_count)
    SVN_ERR(svn_cmdline_fprintf(stderr, pool,
                                Q_("Dropped %d revision.\n\n",
                                   "Dropped %d revisions.\n\n",
                                   pb->rev_drop_count),
                                pb->rev_drop_count));

  if (pb->do_renumber_revs)
    {
      apr_pool_t *subpool = svn_pool_create(pool);
      SVN_ERR(svn_cmdline_fputs(_("Revisions renumbered as follows:\n"),
                                stderr, subpool));

      /* Get the keys of the hash, sort them, then print the hash keys
         and values, sorted by keys. */
      num_keys = apr_hash_count(pb->renumber_history);
      keys = apr_array_make(pool, num_keys + 1, sizeof(svn_revnum_t));
      for (hi = apr_hash_first(pool, pb->renumber_history);
           hi;
           hi = apr_hash_next(hi))
        {
          const svn_revnum_t *revnum = apr_hash_this_key(hi);

          APR_ARRAY_PUSH(keys, svn_revnum_t) = *revnum;
        }
      svn_sort__array(keys, svn_sort_compare_revisions);
      for (i = 0; i < keys->nelts; i++)
        {
          svn_revnum_t this_key;
          struct revmap_t *this_val;

          svn_pool_clear(subpool);
          this_key = APR_ARRAY_IDX(keys, i, svn_revnum_t);
          this_val = apr_hash_get(pb->renumber_history, &this_key,
                                  sizeof(this_key));
          if (this_val->was_dropped)
            SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                        _("   %ld => (dropped)\n"),
                                        this_key));
          else
            SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                        "   %ld => %ld\n",
                                        this_key, this_val->rev));
        }
      SVN_ERR(svn_cmdline_fputs("\n", stderr, subpool));
      svn_pool_destroy(subpool);
    }

  if ((num_keys = apr_hash_count(pb->dropped_nodes)))
    {
      apr_pool_t *subpool = svn_pool_create(pool);
      SVN_ERR(svn_cmdline_fprintf(stderr, subpool,
                                  Q_("Dropped %d node:\n",
                                     "Dropped %d nodes:\n",
                                     num_keys),
                                  num_keys));

      /* Get the keys of the hash, sort them, then print the hash keys
         and values, sorted by keys. */
      keys = apr_array_make(pool, num_keys + 1, sizeof(const char *));
      for (hi = apr_hash_first(pool, pb->dropped_nodes);
           hi;
           hi = apr_hash_next(hi))
        {
          const char *path = apr_hash_this_key(hi);

          APR_ARRAY_PUSH(keys, const char *) = path;
        }
      svn_sort__array(keys, svn_sort_compare_paths);
      for (i = 0; i < keys->nelts; i++)
        {
          svn_pool_clear(subpool);
          SVN_ERR(svn_cmdline_fprintf
                  (stderr, subpool, "   '%s'\n",
                   (const char *)APR_ARRAY_IDX(keys, i, const char *)));
        }
      SVN_ERR(svn_cmdline_fputs("\n", stderr, subpool));
      svn_pool_destroy(subpool);
    }

  return SVN_NO_ERROR;
}

/* This implements `exclude' subcommand. */
static svn_error_t *
subcommand_exclude(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
  return do_filter(os, baton, TRUE, pool);
}

/* This implements `include` subcommand. */
static svn_error_t *
subcommand_include(apr_getopt_t *os, void *baton, apr_pool_t *pool)
{
  return do_filter(os, baton, FALSE, pool);
}

/** Main. **/

/*
 * On success, leave *EXIT_CODE untouched and return SVN_NO_ERROR. On error,
 * either return an error to be displayed, or set *EXIT_CODE to non-zero and
 * return SVN_NO_ERROR.
 */
static svn_error_t *
sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
  svn_error_t *err;
  apr_status_t apr_err;

  const svn_opt_subcommand_desc2_t *subcommand = NULL;
  struct svndumpfilter_opt_state opt_state;
  apr_getopt_t *os;
  int opt_id;
  apr_array_header_t *received_opts;
  int i;

  /* Check library versions */
  SVN_ERR(check_lib_versions());

  received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));

  /* Initialize the FS library. */
  SVN_ERR(svn_fs_initialize(pool));

  if (argc <= 1)
    {
      SVN_ERR(subcommand_help(NULL, NULL, pool));
      *exit_code = EXIT_FAILURE;
      return SVN_NO_ERROR;
    }

  /* Initialize opt_state. */
  memset(&opt_state, 0, sizeof(opt_state));
  opt_state.start_revision.kind = svn_opt_revision_unspecified;
  opt_state.end_revision.kind = svn_opt_revision_unspecified;

  /* Parse options. */
  SVN_ERR(svn_cmdline__getopt_init(&os, argc, argv, pool));

  os->interleave = 1;
  while (1)
    {
      const char *opt_arg;

      /* Parse the next option. */
      apr_err = apr_getopt_long(os, options_table, &opt_id, &opt_arg);
      if (APR_STATUS_IS_EOF(apr_err))
        break;
      else if (apr_err)
        {
          SVN_ERR(subcommand_help(NULL, NULL, pool));
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }

      /* Stash the option code in an array before parsing it. */
      APR_ARRAY_PUSH(received_opts, int) = opt_id;

      switch (opt_id)
        {
        case 'h':
        case '?':
          opt_state.help = TRUE;
          break;
        case svndumpfilter__version:
          opt_state.version = TRUE;
          break;
        case svndumpfilter__quiet:
          opt_state.quiet = TRUE;
          break;
        case svndumpfilter__glob:
          opt_state.glob = TRUE;
          break;
        case svndumpfilter__drop_empty_revs:
          opt_state.drop_empty_revs = TRUE;
          break;
        case svndumpfilter__drop_all_empty_revs:
          opt_state.drop_all_empty_revs = TRUE;
          break;
        case svndumpfilter__renumber_revs:
          opt_state.renumber_revs = TRUE;
          break;
        case svndumpfilter__preserve_revprops:
          opt_state.preserve_revprops = TRUE;
          break;
        case svndumpfilter__skip_missing_merge_sources:
          opt_state.skip_missing_merge_sources = TRUE;
          break;
        case svndumpfilter__targets:
          SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.targets_file,
                                          opt_arg, pool));
          break;
        default:
          {
            SVN_ERR(subcommand_help(NULL, NULL, pool));
            *exit_code = EXIT_FAILURE;
            return SVN_NO_ERROR;
          }
        }  /* close `switch' */
    }  /* close `while' */

  /* Disallow simultaneous use of both --drop-empty-revs and
     --drop-all-empty-revs. */
  if (opt_state.drop_empty_revs && opt_state.drop_all_empty_revs)
    {
      return svn_error_create(SVN_ERR_CL_MUTUALLY_EXCLUSIVE_ARGS,
                              NULL,
                              _("--drop-empty-revs cannot be used with "
                                "--drop-all-empty-revs"));
    }

  /* If the user asked for help, then the rest of the arguments are
     the names of subcommands to get help on (if any), or else they're
     just typos/mistakes.  Whatever the case, the subcommand to
     actually run is subcommand_help(). */
  if (opt_state.help)
    subcommand = svn_opt_get_canonical_subcommand2(cmd_table, "help");

  /* If we're not running the `help' subcommand, then look for a
     subcommand in the first argument. */
  if (subcommand == NULL)
    {
      if (os->ind >= os->argc)
        {
          if (opt_state.version)
            {
              /* Use the "help" subcommand to handle the "--version" option. */
              static const svn_opt_subcommand_desc2_t pseudo_cmd =
                { "--version", subcommand_help, {0}, "",
                  {svndumpfilter__version,  /* must accept its own option */
                   svndumpfilter__quiet,
                  } };

              subcommand = &pseudo_cmd;
            }
          else
            {
              svn_error_clear(svn_cmdline_fprintf
                              (stderr, pool,
                               _("Subcommand argument required\n")));
              SVN_ERR(subcommand_help(NULL, NULL, pool));
              *exit_code = EXIT_FAILURE;
              return SVN_NO_ERROR;
            }
        }
      else
        {
          const char *first_arg;

          SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
                                          pool));
          subcommand = svn_opt_get_canonical_subcommand2(cmd_table, first_arg);
          if (subcommand == NULL)
            {
              svn_error_clear(
                svn_cmdline_fprintf(stderr, pool,
                                    _("Unknown subcommand: '%s'\n"),
                                    first_arg));
              SVN_ERR(subcommand_help(NULL, NULL, pool));
              *exit_code = EXIT_FAILURE;
              return SVN_NO_ERROR;
            }
        }
    }

  /* If there's a second argument, it's probably [one of] prefixes.
     Every subcommand except `help' requires at least one, so we parse
     them out here and store in opt_state. */

  if (subcommand->cmd_func != subcommand_help)
    {

      opt_state.prefixes = apr_array_make(pool, os->argc - os->ind,
                                          sizeof(const char *));
      for (i = os->ind ; i< os->argc; i++)
        {
          const char *prefix;

          /* Ensure that each prefix is UTF8-encoded, in internal
             style, and absolute. */
          SVN_ERR(svn_utf_cstring_to_utf8(&prefix, os->argv[i], pool));
          prefix = svn_relpath__internal_style(prefix, pool);
          if (prefix[0] != '/')
            prefix = apr_pstrcat(pool, "/", prefix, SVN_VA_NULL);
          APR_ARRAY_PUSH(opt_state.prefixes, const char *) = prefix;
        }

      if (opt_state.targets_file)
        {
          svn_stringbuf_t *buffer, *buffer_utf8;
          apr_array_header_t *targets = apr_array_make(pool, 0,
                                                       sizeof(const char *));

          /* We need to convert to UTF-8 now, even before we divide
             the targets into an array, because otherwise we wouldn't
             know what delimiter to use for svn_cstring_split().  */
          SVN_ERR(svn_stringbuf_from_file2(&buffer, opt_state.targets_file,
                                           pool));
          SVN_ERR(svn_utf_stringbuf_to_utf8(&buffer_utf8, buffer, pool));

          targets = apr_array_append(pool,
                         svn_cstring_split(buffer_utf8->data, "\n\r",
                                           TRUE, pool),
                         targets);

          for (i = 0; i < targets->nelts; i++)
            {
              const char *prefix = APR_ARRAY_IDX(targets, i, const char *);
              if (prefix[0] != '/')
                prefix = apr_pstrcat(pool, "/", prefix, SVN_VA_NULL);
              APR_ARRAY_PUSH(opt_state.prefixes, const char *) = prefix;
            }
        }

      if (apr_is_empty_array(opt_state.prefixes))
        {
          svn_error_clear(svn_cmdline_fprintf
                          (stderr, pool,
                           _("\nError: no prefixes supplied.\n")));
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }
    }

  /* Check that the subcommand wasn't passed any inappropriate options. */
  for (i = 0; i < received_opts->nelts; i++)
    {
      opt_id = APR_ARRAY_IDX(received_opts, i, int);

      /* All commands implicitly accept --help, so just skip over this
         when we see it. Note that we don't want to include this option
         in their "accepted options" list because it would be awfully
         redundant to display it in every commands' help text. */
      if (opt_id == 'h' || opt_id == '?')
        continue;

      if (! svn_opt_subcommand_takes_option3(subcommand, opt_id, NULL))
        {
          const char *optstr;
          const apr_getopt_option_t *badopt =
            svn_opt_get_option_from_code2(opt_id, options_table, subcommand,
                                          pool);
          svn_opt_format_option(&optstr, badopt, FALSE, pool);
          if (subcommand->name[0] == '-')
            SVN_ERR(subcommand_help(NULL, NULL, pool));
          else
            svn_error_clear(svn_cmdline_fprintf
                            (stderr, pool,
                             _("Subcommand '%s' doesn't accept option '%s'\n"
                               "Type 'svndumpfilter help %s' for usage.\n"),
                             subcommand->name, optstr, subcommand->name));
          *exit_code = EXIT_FAILURE;
          return SVN_NO_ERROR;
        }
    }

  /* Run the subcommand. */
  err = (*subcommand->cmd_func)(os, &opt_state, pool);
  if (err)
    {
      /* For argument-related problems, suggest using the 'help'
         subcommand. */
      if (err->apr_err == SVN_ERR_CL_INSUFFICIENT_ARGS
          || err->apr_err == SVN_ERR_CL_ARG_PARSING_ERROR)
        {
          err = svn_error_quick_wrap(err,
                                     _("Try 'svndumpfilter help' for more "
                                       "info"));
        }
      return err;
    }

  return SVN_NO_ERROR;
}

int
main(int argc, const char *argv[])
{
  apr_pool_t *pool;
  int exit_code = EXIT_SUCCESS;
  svn_error_t *err;

  /* Initialize the app. */
  if (svn_cmdline_init("svndumpfilter", stderr) != EXIT_SUCCESS)
    return EXIT_FAILURE;

  /* Create our top-level pool.  Use a separate mutexless allocator,
   * given this application is single threaded.
   */
  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));

  err = sub_main(&exit_code, argc, argv, pool);

  /* Flush stdout and report if it fails. It would be flushed on exit anyway
     but this makes sure that output is not silently lost if it fails. */
  err = svn_error_compose_create(err, svn_cmdline_fflush(stdout));

  if (err)
    {
      exit_code = EXIT_FAILURE;
      svn_cmdline_handle_exit_error(err, NULL, "svndumpfilter: ");
    }

  svn_pool_destroy(pool);
  return exit_code;
}


text:
package com.vladsch.flexmark.ext.jekyll.front.matter;

import com.vladsch.flexmark.util.ast.VisitHandler;

public class JekyllFrontMatterVisitorExt {
    public static <V extends JekyllFrontMatterVisitor> VisitHandler<?>[] VISIT_HANDLERS(V visitor) {
        return new VisitHandler<?>[] {
                new VisitHandler<>(JekyllFrontMatterBlock.class, visitor::visit),
        };
    }
}

redpajama_set_name: RedPajamaGithub


text:
@interface ElGiftViewController : ElBaseViewController

@end


text:
package com.xmomen.module.report.controller;

import com.xmomen.framework.utils.StringUtilsExt;
import com.xmomen.module.base.constant.AppConstants;
import com.xmomen.module.order.model.OrderQuery;
import com.xmomen.module.order.service.OrderService;
import com.xmomen.module.report.model.*;
import com.xmomen.module.report.service.ReportOrderService;
import com.xmomen.module.stockdaily.model.StockDailyModel;
import com.xmomen.module.stockdaily.service.StockDailyService;
import org.apache.shiro.SecurityUtils;
import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.vo.NormalExcelConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.List;

/**
 * Created by tanxinzheng on 16/9/3.
 */
@Controller
public class OrderReportController {

    @Autowired
    ReportOrderService reportOrderService;

    @Autowired
    StockDailyService stockDailyService;

    /**
     * 订单导出
     *
     * @param modelMap
     * @return
     */
    @RequestMapping(value = "/report/order", method = RequestMethod.GET)
    public String exportOrder(
            @RequestParam(value = "beginTime", required = false) String beginTime,
            @RequestParam(value = "endTime", required = false) String endTime,
            @RequestParam(value = "companyId", required = false) Integer companyId,
            @RequestParam(value = "managerId", required = false) Integer managerId,
            ModelMap modelMap) {
        ReportQuery reportQuery = new ReportQuery();
        if (StringUtilsExt.isNotBlank(beginTime)) {
            reportQuery.setBeginTime(beginTime);
        }
        if (StringUtilsExt.isNotBlank(endTime)) {
            reportQuery.setEndTime(endTime);
        }
        reportQuery.setCompanyId(companyId);
        reportQuery.setManagerId(managerId);
        //客服经理过滤 如果有客服组权限则不过滤
        if (SecurityUtils.getSubject().hasRole(AppConstants.CUSTOMER_MANAGER_PERMISSION_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.CUSTOMER_PERMISSION_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.HOU_TAI_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.ADMIN)
                && !SecurityUtils.getSubject().hasRole(AppConstants.SUPER_ADMIN)
                && !SecurityUtils.getSubject().hasRole(AppConstants.CWU)
                && !SecurityUtils.getSubject().hasRole(AppConstants.WULIUZXB)) {
            Integer userId = (Integer) SecurityUtils.getSubject().getSession().getAttribute(AppConstants.SESSION_USER_ID_KEY);
            reportQuery.setManagerId(userId);
        }
        List<OrderReport> list = reportOrderService.getOrderReportList(reportQuery);
        String[] beginTimes = beginTime.split("-");
        String[] endTimes = endTime.split("-");
        modelMap.put(NormalExcelConstants.FILE_NAME, beginTimes[0] + "年" + beginTimes[1] + "月" + beginTimes[2] + "日-" + endTimes[0] + "年" + endTimes[1] + "月" + endTimes[2] + "日订单报表");
        modelMap.put(NormalExcelConstants.PARAMS, new ExportParams());
        modelMap.put(NormalExcelConstants.CLASS, OrderReport.class);
        modelMap.put(NormalExcelConstants.DATA_LIST, list);
        return NormalExcelConstants.JEECG_EXCEL_VIEW;
    }

    /**
     * 物流导出
     *
     * @param modelMap
     * @return
     */
    @RequestMapping(value = "/report/express", method = RequestMethod.GET)
    public String exportExpress(
            @RequestParam(value = "beginTime", required = false) String beginTime,
            @RequestParam(value = "endTime", required = false) String endTime,
            @RequestParam(value = "companyId", required = false) Integer companyId,
            @RequestParam(value = "managerId", required = false) Integer managerId,
            ModelMap modelMap) {
        ReportQuery reportQuery = new ReportQuery();
        if (StringUtilsExt.isNotBlank(beginTime)) {
            reportQuery.setBeginTime(beginTime);
        }
        if (StringUtilsExt.isNotBlank(endTime)) {
            reportQuery.setEndTime(endTime);
        }
        reportQuery.setCompanyId(companyId);
        reportQuery.setManagerId(managerId);
        //客服经理过滤 如果有客服组权限则不过滤
        if (SecurityUtils.getSubject().hasRole(AppConstants.CUSTOMER_MANAGER_PERMISSION_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.CUSTOMER_PERMISSION_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.HOU_TAI_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.ADMIN)
                && !SecurityUtils.getSubject().hasRole(AppConstants.SUPER_ADMIN)
                && !SecurityUtils.getSubject().hasRole(AppConstants.CWU)
                && !SecurityUtils.getSubject().hasRole(AppConstants.WULIUZXB)) {
            Integer userId = (Integer) SecurityUtils.getSubject().getSession().getAttribute(AppConstants.SESSION_USER_ID_KEY);
            reportQuery.setManagerId(userId);
        }
        List<ExpressReport> list = reportOrderService.getExpressReportList(reportQuery);
        String[] beginTimes = beginTime.split("-");
        String[] endTimes = endTime.split("-");
        modelMap.put(NormalExcelConstants.FILE_NAME, beginTimes[0] + "年" + beginTimes[1] + "月" + beginTimes[2] + "日-" + endTimes[0] + "年" + endTimes[1] + "月" + endTimes[2] + "日物流报表");
        modelMap.put(NormalExcelConstants.PARAMS, new ExportParams());
        modelMap.put(NormalExcelConstants.CLASS, ExpressReport.class);
        modelMap.put(NormalExcelConstants.DATA_LIST, list);
        return NormalExcelConstants.JEECG_EXCEL_VIEW;
    }

    /**
     * 财务导出
     *
     * @param modelMap
     * @return
     */
    @RequestMapping(value = "/report/finance", method = RequestMethod.GET)
    public String exportFinance(
            @RequestParam(value = "beginTime", required = false) String beginTime,
            @RequestParam(value = "endTime", required = false) String endTime,
            @RequestParam(value = "companyId", required = false) Integer companyId,
            @RequestParam(value = "managerId", required = false) Integer managerId,
            ModelMap modelMap) {
        ReportQuery reportQuery = new ReportQuery();
        if (StringUtilsExt.isNotBlank(beginTime)) {
            reportQuery.setBeginTime(beginTime);
        }
        if (StringUtilsExt.isNotBlank(endTime)) {
            reportQuery.setEndTime(endTime);
        }
        reportQuery.setCompanyId(companyId);
        reportQuery.setManagerId(managerId);
        //客服经理过滤 如果有客服组权限则不过滤
        if (SecurityUtils.getSubject().hasRole(AppConstants.CUSTOMER_MANAGER_PERMISSION_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.CUSTOMER_PERMISSION_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.HOU_TAI_CODE)
                && !SecurityUtils.getSubject().hasRole(AppConstants.ADMIN)
                && !SecurityUtils.getSubject().hasRole(AppConstants.SUPER_ADMIN)
                && !SecurityUtils.getSubject().hasRole(AppConstants.CWU)
                && !SecurityUtils.getSubject().hasRole(AppConstants.WULIUZXB)) {
            Integer userId = (Integer) SecurityUtils.getSubject().getSession().getAttribute(AppConstants.SESSION_USER_ID_KEY);
            reportQuery.setManagerId(userId);
        }
        List<FinanceReport> list = reportOrderService.getFinanceReportList(reportQuery);
        String[] beginTimes = beginTime.split("-");
        String[] endTimes = endTime.split("-");
        modelMap.put(NormalExcelConstants.FILE_NAME, beginTimes[0] + "年" + beginTimes[1] + "月" + beginTimes[2] + "日-" + endTimes[0] + "年" + endTimes[1] + "月" + endTimes[2] + "日财务报表");
        modelMap.put(NormalExcelConstants.PARAMS, new ExportParams());
        modelMap.put(NormalExcelConstants.CLASS, FinanceReport.class);
        modelMap.put(NormalExcelConstants.DATA_LIST, list);
        return NormalExcelConstants.JEECG_EXCEL_VIEW;
    }

    /**
     * 库存快照导出
     *
     * @param modelMap
     * @return
     */
    @RequestMapping(value = "/report/stockDaily", method = RequestMethod.GET)
    public String exportStockDaily(
            @RequestParam(value = "beginTime", required = false) String beginTime,
            @RequestParam(value = "endTime", required = false) String endTime,
            ModelMap modelMap) {
        ReportQuery reportQuery = new ReportQuery();
        if (StringUtilsExt.isNotBlank(beginTime)) {
            reportQuery.setBeginTime(beginTime);
        }
        if (StringUtilsExt.isNotBlank(endTime)) {
            reportQuery.setEndTime(endTime);
        }

        List<StockDailyReport> list = stockDailyService.getStockDailyReport(reportQuery);
        String[] beginTimes = beginTime.split("-");
        String[] endTimes = endTime.split("-");
        modelMap.put(NormalExcelConstants.FILE_NAME, beginTimes[0] + "年" + beginTimes[1] + "月" + beginTimes[2] + "日-" + endTimes[0] + "年" + endTimes[1] + "月" + endTimes[2] + "日库存快照报表");
        modelMap.put(NormalExcelConstants.PARAMS, new ExportParams());
        modelMap.put(NormalExcelConstants.CLASS, StockDailyReport.class);
        modelMap.put(NormalExcelConstants.DATA_LIST, list);
        return NormalExcelConstants.JEECG_EXCEL_VIEW;
    }
}


text:
<?php
/**
 * Global Configuration Override
 *
 * You can use this file for overriding configuration values from modules, etc.
 * You would place values in here that are agnostic to the environment and not
 * sensitive to security.
 *
 * @NOTE: In practice, this file will typically be INCLUDED in your source
 * control, so do not include passwords or other sensitive information in this
 * file.
 */
return array(
    'db' => array(
        'driver'         => 'Pdo',
        'dsn'            => 'mysql:dbname=users;host=localhost',
        'driver_options' => array(
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
        ),
    ),
    'service_manager' => array(
        'factories' => array(
            'Zend\Db\Adapter\Adapter'
                    => 'Zend\Db\Adapter\AdapterServiceFactory',
        ),
    ),
    'translator' => array(
        'locale' => 'en_US',
        'translation_file_patterns' => array(
            array(
                'type'     => 'phpArray',
                'base_dir' => __DIR__ . '/../../language',
                'pattern'  => '%s.php',
            ),
        ),
    )
);


text:
#include "common.h"
#include "config.h"
#include "console.h"
#include "link_defs.h"
#include "printf.h"
#include "queue.h"
#include "registers.h"
#include "task.h"
#include "timer.h"
#include "util.h"
#include "usb_descriptor.h"
#include "usb_hw.h"

/* Console output macro */
#define CPRINTF(format, args...) cprintf(CC_USB, format, ##args)
#define USB_CONSOLE_TIMEOUT_US (30 * MSEC)

static int last_tx_ok = 1;

static int is_reset;
static int is_enabled = 1;
static int is_readonly;

/* USB-Serial descriptors */
const struct usb_interface_descriptor USB_IFACE_DESC(USB_IFACE_CONSOLE) = {
	.bLength = USB_DT_INTERFACE_SIZE,
	.bDescriptorType = USB_DT_INTERFACE,
	.bInterfaceNumber = USB_IFACE_CONSOLE,
	.bAlternateSetting = 0,
	.bNumEndpoints = 2,
	.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
	.bInterfaceSubClass = USB_SUBCLASS_GOOGLE_SERIAL,
	.bInterfaceProtocol = USB_PROTOCOL_GOOGLE_SERIAL,
	.iInterface = USB_STR_CONSOLE_NAME,
};
const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 0) = {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = 0x80 | USB_EP_CONSOLE,
	.bmAttributes = 0x02 /* Bulk IN */,
	.wMaxPacketSize = USB_MAX_PACKET_SIZE,
	.bInterval = 10,
};
const struct usb_endpoint_descriptor USB_EP_DESC(USB_IFACE_CONSOLE, 1) = {
	.bLength = USB_DT_ENDPOINT_SIZE,
	.bDescriptorType = USB_DT_ENDPOINT,
	.bEndpointAddress = USB_EP_CONSOLE,
	.bmAttributes = 0x02 /* Bulk OUT */,
	.wMaxPacketSize = USB_MAX_PACKET_SIZE,
	.bInterval = 0
};

static uint8_t ep_buf_tx[USB_MAX_PACKET_SIZE];
static uint8_t ep_buf_rx[USB_MAX_PACKET_SIZE];

static struct queue const tx_q = QUEUE_NULL(256, uint8_t);
static struct queue const rx_q = QUEUE_NULL(USB_MAX_PACKET_SIZE, uint8_t);

struct dwc_usb_ep ep_console_ctl = {
	.max_packet = USB_MAX_PACKET_SIZE,
	.tx_fifo = USB_EP_CONSOLE,
	.out_pending = 0,
	.out_data = 0,
	.out_databuffer = ep_buf_tx,
	.out_databuffer_max = sizeof(ep_buf_tx),
	.in_packets = 0,
	.in_pending = 0,
	.in_data = 0,
	.in_databuffer = ep_buf_rx,
	.in_databuffer_max = sizeof(ep_buf_rx),
};

/* Let the USB HW IN-to-host FIFO transmit some bytes */
static void usb_enable_tx(int len)
{
	struct dwc_usb_ep *ep = &ep_console_ctl;

	ep->in_data = ep->in_databuffer;
	ep->in_packets = 1;
	ep->in_pending = len;

	GR_USB_DIEPTSIZ(USB_EP_CONSOLE) = 0;

	GR_USB_DIEPTSIZ(USB_EP_CONSOLE) |= DXEPTSIZ_PKTCNT(1);
	GR_USB_DIEPTSIZ(USB_EP_CONSOLE) |= DXEPTSIZ_XFERSIZE(len);
	GR_USB_DIEPDMA(USB_EP_CONSOLE) = (uint32_t)ep->in_data;

	GR_USB_DIEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
}

/* Let the USB HW OUT-from-host FIFO receive some bytes */
static void usb_enable_rx(int len)
{
	struct dwc_usb_ep *ep = &ep_console_ctl;

	ep->out_data = ep->out_databuffer;
	ep->out_pending = 0;

	GR_USB_DOEPTSIZ(USB_EP_CONSOLE) = 0;
	GR_USB_DOEPTSIZ(USB_EP_CONSOLE) |= DXEPTSIZ_PKTCNT(1);
	GR_USB_DOEPTSIZ(USB_EP_CONSOLE) |= DXEPTSIZ_XFERSIZE(len);
	GR_USB_DOEPDMA(USB_EP_CONSOLE) = (uint32_t)ep->out_data;

	GR_USB_DOEPCTL(USB_EP_CONSOLE) |= DXEPCTL_CNAK | DXEPCTL_EPENA;
}

/* True if the HW Rx/OUT FIFO has bytes for us. */
static inline int rx_fifo_is_ready(void)
{
	struct dwc_usb_ep *ep = &ep_console_ctl;

	return ep->out_pending;
}

/*
 * This function tries to shove new bytes from the USB host into the queue for
 * consumption elsewhere. It is invoked either by a HW interrupt (telling us we
 * have new bytes from the USB host), or by whoever is reading bytes out of the
 * other end of the queue (telling us that there's now more room in the queue
 * if we still have bytes to shove in there).
 */
char buffer[65];
static void rx_fifo_handler(void)
{
	struct dwc_usb_ep *ep = &ep_console_ctl;

	int rx_in_fifo;
	size_t added;

	if (!rx_fifo_is_ready())
		return;

	rx_in_fifo = ep->out_pending;
	added = QUEUE_ADD_UNITS(&rx_q, ep->out_databuffer, rx_in_fifo);

	if (added != rx_in_fifo)
		CPRINTF("DROP CONSOLE: %d/%d process\n", added, rx_in_fifo);

	/* wake-up the console task */
	console_has_input();

	usb_enable_rx(USB_MAX_PACKET_SIZE);
}
DECLARE_DEFERRED(rx_fifo_handler);

/* Rx/OUT interrupt handler */
static void con_ep_rx(void)
{
	struct dwc_usb_ep *ep = &ep_console_ctl;

	if (GR_USB_DOEPCTL(USB_EP_CONSOLE) & DXEPCTL_EPENA)
		return;

	/* Bytes received decrement DOEPTSIZ XFERSIZE */
	if (GR_USB_DOEPINT(USB_EP_CONSOLE) & DOEPINT_XFERCOMPL) {
		ep->out_pending =
			ep->max_packet - (GR_USB_DOEPTSIZ(USB_EP_CONSOLE) &
					  GC_USB_DOEPTSIZ1_XFERSIZE_MASK);
	}

	/* Wake up the Rx FIFO handler */
	hook_call_deferred(&rx_fifo_handler_data, 0);

	/* clear the RX/OUT interrupts */
	GR_USB_DOEPINT(USB_EP_CONSOLE) = 0xffffffff;
}

/* True if the Tx/IN FIFO can take some bytes from us. */
static inline int tx_fifo_is_ready(void)
{
	return !(GR_USB_DIEPCTL(USB_EP_CONSOLE) & DXEPCTL_EPENA);
}

/* Try to send some bytes to the host */
static void tx_fifo_handler(void)
{
	struct dwc_usb_ep *ep = &ep_console_ctl;
	size_t count;

	if (!is_reset)
		return;

	/* If the HW FIFO isn't ready, then we can't do anything right now. */
	if (!tx_fifo_is_ready())
		return;

	count = QUEUE_REMOVE_UNITS(&tx_q, ep->in_databuffer,
				   USB_MAX_PACKET_SIZE);
	if (count)
		usb_enable_tx(count);
}
DECLARE_DEFERRED(tx_fifo_handler);

static void handle_output(void)
{
	/* Wake up the Tx FIFO handler */
	hook_call_deferred(&tx_fifo_handler_data, 0);
}

/* Tx/IN interrupt handler */
static void con_ep_tx(void)
{
	/* Wake up the Tx FIFO handler */
	hook_call_deferred(&tx_fifo_handler_data, 0);

	/* clear the Tx/IN interrupts */
	GR_USB_DIEPINT(USB_EP_CONSOLE) = 0xffffffff;
}

static void ep_event(enum usb_ep_event evt)
{
	if (evt != USB_EVENT_RESET)
		return;

	epN_reset(USB_EP_CONSOLE);

	is_reset = 1;

	/* Flush any queued data */
	hook_call_deferred(&tx_fifo_handler_data, 0);
	hook_call_deferred(&rx_fifo_handler_data, 0);

	usb_enable_rx(USB_MAX_PACKET_SIZE);
}

USB_DECLARE_EP(USB_EP_CONSOLE, con_ep_tx, con_ep_rx, ep_event);

static int usb_wait_console(void)
{
	timestamp_t deadline = get_time();
	int wait_time_us = 1;

	if (!is_enabled || !tx_fifo_is_ready())
		return EC_SUCCESS;

	deadline.val += USB_CONSOLE_TIMEOUT_US;

	/*
	 * If the USB console is not used, Tx buffer would never free up.
	 * In this case, let's drop characters immediately instead of sitting
	 * for some time just to time out. On the other hand, if the last
	 * Tx is good, it's likely the host is there to receive data, and
	 * we should wait so that we don't clobber the buffer.
	 */
	if (last_tx_ok) {
		while (queue_space(&tx_q) < USB_MAX_PACKET_SIZE || !is_reset) {
			if (timestamp_expired(deadline, NULL) ||
			    in_interrupt_context()) {
				last_tx_ok = 0;
				return EC_ERROR_TIMEOUT;
			}
			if (wait_time_us < MSEC)
				udelay(wait_time_us);
			else
				usleep(wait_time_us);
			wait_time_us *= 2;
		}

		return EC_SUCCESS;
	}

	last_tx_ok = queue_space(&tx_q);
	return EC_SUCCESS;
}
static int __tx_char(void *context, int c)
{
	struct queue *state = (struct queue *)context;

	if (c == '\n' && __tx_char(state, '\r'))
		return 1;

	QUEUE_ADD_UNITS(state, &c, 1);
	return 0;
}

/*
 * Public USB console implementation below.
 */
int usb_getc(void)
{
	int c;

	if (!is_enabled)
		return -1;

	if (QUEUE_REMOVE_UNITS(&rx_q, &c, 1))
		return c;

	return -1;
}

int usb_puts(const char *outstr)
{
	int ret;
	struct queue state;

	if (is_readonly)
		return EC_SUCCESS;

	ret = usb_wait_console();
	if (ret)
		return ret;

	state = tx_q;
	while (*outstr)
		if (__tx_char(&state, *outstr++))
			break;

	if (queue_count(&state))
		handle_output();

	return *outstr ? EC_ERROR_OVERFLOW : EC_SUCCESS;
}

int usb_putc(int c)
{
	char string[2];

	string[0] = c;
	string[1] = '\0';
	return usb_puts(string);
}

int usb_vprintf(const char *format, va_list args)
{
	int ret;
	struct queue state;

	if (is_readonly)
		return EC_SUCCESS;

	ret = usb_wait_console();
	if (ret)
		return ret;

	state = tx_q;
	ret = vfnprintf(__tx_char, &state, format, args);

	if (queue_count(&state))
		handle_output();

	return ret;
}

void usb_console_enable(int enabled, int readonly)
{
	is_enabled = enabled;
	is_readonly = readonly;
}


text:
hide: false
next: false
sort: 1
title:  Leap into Hoon
---

<list></list>


text:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.motion.widget.MotionLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layoutDescription="@xml/verification_scene_310"
    app:layout_optimizationLevel="none"
    android:background="#777"
    tools:context=".VerificationActivity">

    <androidx.constraintlayout.utils.widget.ImageFilterView
        android:id="@+id/imageFilterView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:src="@drawable/img3"
        app:brightness=".9"

        app:saturation="0.7"

        app:imageZoom="1.5"
        app:layout_constraintBottom_toBottomOf="@id/label5"
        app:layout_constraintEnd_toEndOf="@id/label5"
        app:layout_constraintLeft_toLeftOf="@id/label5"
        app:layout_constraintRight_toRightOf="@id/label5"
        app:layout_constraintStart_toStartOf="@id/label5"
        app:layout_constraintTop_toTopOf="@id/label5" />

    <androidx.constraintlayout.utils.widget.MotionLabel
        android:id="@+id/label5"
        android:layout_width="0dp"
        android:layout_height="300dp"
        app:textPanX="-1.3"
        app:textBackgroundZoom="1.5"
        android:text="A world of Animated Text"
        android:textSize="240sp"
        app:textBackground="@drawable/img3"
        app:textureEffect="frost"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />

</androidx.constraintlayout.motion.widget.MotionLayout>


text:
import React, { PropTypes as T } from 'react'
import { contains } from 'ramda'
import CheckboxStandalone from '../form/CheckboxStandalone'
import Sortable from '../Sortable'
import Icon from '../Icon'
import css from './styles.css'

function addToSet(x, set) {
  return set.concat([x])
}

function removeFromSet(x, set) {
  return set.filter(y => y !== x)
}

const Item = T.shape({
  id: T.oneOfType([T.number, T.string]).isRequired
})

export default React.createClass({

  displayName: 'DataTable',

  propTypes: {
    titles: T.arrayOf(T.node).isRequired,
    items: T.arrayOf(Item.isRequired).isRequired,
    itemToCells: T.func.isRequired, // Item => [T.node]
    disabled: T.bool,
    selectable: T.bool,
    onSelectChange: T.func,
    selectedItems: T.arrayOf(Item.isRequired),
    sortable: T.bool,
    onSortingChange: T.func,
    onSortingCommitReq: T.func
  },

  getDefaultProps() {
    return {
      disabled: false,
      selectable: false,
      onSelectChange: () => {},
      selectedItems: [],
      sortable: false,
      onSortingChange: () => {},
      onSortingCommitReq: () => {}
    }
  },

  renderTitle(x, i) {
    return {x}
  },

  renderCell(x, i) {
    return {x}
  },

  renderRowCheckbox(item, selected) {
    const { disabled, selectable, onSelectChange, selectedItems } = this.props
    return selectable &&
      <CheckboxStandalone
        disabled={disabled}
        value={selected}
        onChange={x => onSelectChange((x ? addToSet : removeFromSet)(item, selectedItems))}
        className="mdl-data-table__select"
      />

  },

  renderTopCheckbox() {
    const { items, disabled, selectable, onSelectChange, selectedItems } = this.props
    return selectable &&
      <CheckboxStandalone
        disabled={disabled}
        value={selectedItems.length === items.length}
        onChange={x => onSelectChange(x ? items : [])}
        className="mdl-data-table__select"
      />

  },

  renderRow(item, hide = false, children = null) {
    const { selectedItems, itemToCells } = this.props
    const selected = contains(item, selectedItems)
    return

      {children}
      {this.renderRowCheckbox(item, selected)}
      {itemToCells(item).map(this.renderCell)}

  },

  renderWrap(children) {
    const { titles, sortable } = this.props
    return <table className={`${css.DataTable} mdl-data-table mdl-data-table--selectable is-upgraded`}>

          {sortable && }
          {this.renderTopCheckbox()}
          {titles.map(this.renderTitle)}

        {children}

  },

  renderSortableItem({ item, connectDragSource, connectDropTarget, connectDragPreview, isDragging, disabled }) {
    const handleStyle = {
      cursor: disabled ? 'default' : 'move',
      opacity: disabled ? '0.5' : '1'
    }
    const handle =
      {connectDragSource(

        <Icon name="reorder" />

)}

    return connectDropTarget(connectDragPreview(this.renderRow(item, isDragging, handle)))
  },

  render() {
    const { items, sortable, onSortingChange, onSortingCommitReq, disabled } = this.props
    return sortable
      ? <Sortable
        disabled={disabled}
        items={items}
        onChange={onSortingChange}
        onCommitReq={onSortingCommitReq}
        renderItem={this.renderSortableItem}
        renderWrap={this.renderWrap}
      />
      : this.renderWrap(items.map(x => this.renderRow(x)))
  }

})


text:
package li.strolch.persistence.api;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import li.strolch.agent.api.OrderMap;
import li.strolch.exception.StrolchException;
import li.strolch.model.Order;
import li.strolch.service.api.Command;
import li.strolch.utils.dbc.DBC;

/**
 * @author Robert von Burg <eitch@eitchnet.ch>
 */
public class RemoveOrdersCommand extends Command {

	private List<Order> orders = new ArrayList<>();
	private final List<Order> removed = new ArrayList<>();

	public RemoveOrdersCommand(StrolchTransaction tx) {
		super(tx);
	}

	/**
	 * @param orders
	 * 		the orders to set for removal
	 */
	public void setOrders(List<Order> orders) {
		this.orders = orders;
	}

	/**
	 * @param order
	 * 		the order to add for removal
	 */
	public void addOrder(Order order) {
		this.orders.add(order);
	}

	@Override
	public void validate() {
		DBC.PRE.assertNotEmpty("Orders may not be empty!", this.orders);
	}

	@Override
	public void doCommand() {

		this.orders.forEach(order -> tx().lock(order));

		OrderMap orderMap = tx().getOrderMap();

		this.orders.forEach(order -> {
			if (!orderMap.hasElement(tx(), order.getType(), order.getId())) {
				String msg = "The Order {0} can not be removed as it does not exist!!";
				msg = MessageFormat.format(msg, order.getLocator());
				throw new StrolchException(msg);
			}

			orderMap.remove(tx(), order);
			this.removed.add(order);
		});
	}

	@Override
	public void undo() {
		if (tx().isRollingBack() && !this.removed.isEmpty()) {
			this.orders.forEach(order -> {
				if (tx().isVersioningEnabled())
					tx().getOrderMap().undoVersion(tx(), order);
				else
					tx().getOrderMap().add(tx(), order);
			});
		}
	}
}


text:
package com.ninchat.client.model;

import com.ninchat.client.transport.actions.CreateSession;

/**
 * @author Kari Lavikka
 */
public abstract class SessionCreationMethod {
	public abstract CreateSession getAction();
}


text:
var TransferUnitsRoute = Ember.Route.extend({
  templateName: 'grid/units',
  controllerName: 'grid/units',

  /**
   * Picks the selected Unit from the TransferUnitsRoute model.
   *
   * @param  {Object} params
   * @return {array} array containing the selected Units.
   */
  model: function(params) {
    var units = this.modelFor('transfer');

    var selectedUnitsIds = params.unitIds.split(',');
    var selectedUnits = units.filter(function (unit) {
      Ember.assert('Expected unit.id property to be a number', Ember.typeOf(unit.get('id')) === 'number');
      var contains = selectedUnitsIds.contains(unit.get('id').toString());

      return contains;
    });

    return selectedUnits;
  },

  setupController: function (controller, model) {
    this._super(controller, model);

    this.controllerFor('grid').updateSelection(model);
    this.send('updatedGridSelection', model);
  }
});

export default TransferUnitsRoute;


text:
describe("Ext.data.ErrorCollection", function() {
    var errors;

    beforeEach(function() {
        errors = new Ext.data.ErrorCollection();
    });

    describe("if valid", function() {
        it("should return true to isValid", function() {
            expect(errors.isValid()).toBe(true);
        });
    });

    describe("if not valid", function() {
        beforeEach(function() {
            errors.add('name', 'Foo');
            errors.add('email', 'Bar');
        });

        it("should return false to isValid", function() {
            expect(errors.isValid()).toBe(false);
        });

        it("should return the errors for a single field", function() {
            var getByField = errors.getByField('name');

            expect(getByField.length).toEqual(1);
            expect(getByField[0].field).toEqual('name');
        });
    });
});


text:
import {
  AfterContentInit,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ContentChildren,
  ElementRef,
  EventEmitter,
  Input,
  OnDestroy,
  Optional,
  Output,
  QueryList,
  ViewEncapsulation,
} from '@angular/core';

import { Subject, fromEvent as observableFromEvent } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

import { SkyDropdownItemComponent } from './dropdown-item.component';
import { SkyDropdownComponent } from './dropdown.component';
import { SkyDropdownMenuChange } from './types/dropdown-menu-change';
import { SkyDropdownMessage } from './types/dropdown-message';
import { SkyDropdownMessageType } from './types/dropdown-message-type';

let nextId = 0;
/**
 * Creates a menu that contains dropdown menu items.
 *
 */
@Component({
  selector: 'sky-dropdown-menu',
  templateUrl: './dropdown-menu.component.html',
  styleUrls: ['./dropdown-menu.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
})
export class SkyDropdownMenuComponent implements AfterContentInit, OnDestroy {
  /**
   * Specifies the HTML element ID of the element that labels
   * the dropdown menu. This sets the dropdown menu's `aria-labelledby` attribute
   * [to support accessibility](https://developer.blackbaud.com/skyux/learn/accessibility).
   */
  @Input()
  public ariaLabelledBy: string | undefined;

  /**
   * Specifies an ARIA role for the dropdown menu
   * [to support accessibility](https://developer.blackbaud.com/skyux/learn/accessibility)
   * by indicating how the dropdown menu functions and what it controls. The dropdown button
   * inherits this value to set its `aria-haspopup` property. For information
   * about how an ARIA role indicates what an item represents on a web page, see the
   * [WAI-ARIA roles model](https://www.w3.org/WAI/PF/aria/roles).
   * @default "menu"
   */
  @Input()
  public set ariaRole(value: string | undefined) {
    this.#_ariaRole = value ?? 'menu';
  }

  public get ariaRole(): string {
    return this.#_ariaRole;
  }

  /**
   * Indicates whether to use the browser's native focus function when users navigate through menu
   * items with the keyboard. To disable the native focus function, set this property to `false`.
   * For example, to let users interact with the dropdown menu but keep the keyboard focus on a
   * different element, set this property to `false`.
   * @default true
   * @internal
   */
  @Input()
  public set useNativeFocus(value: boolean | undefined) {
    this.#_useNativeFocus = value ?? true;
  }

  public get useNativeFocus(): boolean {
    return this.#_useNativeFocus;
  }

  /**
   * Fires when the dropdown menu's active index or selected item changes. This event provides an
   * observable to emit changes, and the response is of
   * the SkyDropdownMenuChange type.
   * @internal
   */
  @Output()
  public menuChanges = new EventEmitter<SkyDropdownMenuChange>();

  public dropdownMenuId = `sky-dropdown-menu-${++nextId}`;

  public set menuIndex(value: number) {
    if (value < 0) {
      value = this.menuItems.length - 1;
    }

    if (value >= this.menuItems.length) {
      value = 0;
    }

    this.#_menuIndex = value;
  }

  public get menuIndex(): number {
    return this.#_menuIndex;
  }

  @ContentChildren(SkyDropdownItemComponent, { descendants: true })
  public menuItems!: QueryList<SkyDropdownItemComponent>;

  #ngUnsubscribe = new Subject<void>();

  #_ariaRole = 'menu';

  #_menuIndex = 0;

  #_useNativeFocus = true;

  #changeDetector: ChangeDetectorRef;
  #elementRef: ElementRef;
  #dropdownComponent: SkyDropdownComponent | undefined;

  constructor(
    changeDetector: ChangeDetectorRef,
    elementRef: ElementRef,
    @Optional() dropdownComponent?: SkyDropdownComponent
  ) {
    this.#changeDetector = changeDetector;
    this.#elementRef = elementRef;
    this.#dropdownComponent = dropdownComponent;
  }

  public ngAfterContentInit(): void {
    /* istanbul ignore else */
    if (this.#dropdownComponent) {
      this.#dropdownComponent.menuId = this.dropdownMenuId;
      this.#dropdownComponent.menuAriaRole = this.ariaRole;

      this.#dropdownComponent.messageStream
        ?.pipe(takeUntil(this.#ngUnsubscribe))
        .subscribe((message: SkyDropdownMessage) => {
          switch (message.type) {
            case SkyDropdownMessageType.Open:
            case SkyDropdownMessageType.Close:
              this.reset();
              break;

            case SkyDropdownMessageType.FocusFirstItem:
              this.focusFirstItem();
              break;

            case SkyDropdownMessageType.FocusNextItem:
              this.focusNextItem();
              break;

            case SkyDropdownMessageType.FocusPreviousItem:
              this.focusPreviousItem();
              break;

            case SkyDropdownMessageType.FocusLastItem:
              this.focusLastItem();
              break;
          }
        });

      this.menuChanges
        .pipe(takeUntil(this.#ngUnsubscribe))
        .subscribe((change: SkyDropdownMenuChange) => {
          // Close the dropdown when a menu item is selected.
          if (change.selectedItem) {
            this.#sendMessage(SkyDropdownMessageType.Close);
            this.#sendMessage(SkyDropdownMessageType.FocusTriggerButton);
          }

          if (change.items) {
            // Update the popover style and position whenever the number of items changes.
            this.#sendMessage(SkyDropdownMessageType.Reposition);
          }
        });
    }

    // Reset dropdown whenever the menu items change.
    this.menuItems.changes
      .pipe(takeUntil(this.#ngUnsubscribe))
      .subscribe((items: QueryList<SkyDropdownItemComponent>) => {
        this.reset();
        this.menuChanges.emit({
          items: items.toArray(),
        });
      });

    this.#addEventListeners();
  }

  public ngOnDestroy(): void {
    this.#ngUnsubscribe.next();
    this.#ngUnsubscribe.complete();
  }

  public focusFirstItem(): void {
    if (!this.#hasFocusableItems()) {
      return;
    }

    this.menuIndex = 0;

    const firstItem = this.#getItemByIndex(this.menuIndex);
    if (firstItem && firstItem.isFocusable()) {
      this.#focusItem(firstItem);
    } else {
      this.focusNextItem();
    }
  }

  public focusLastItem(): void {
    if (!this.#hasFocusableItems()) {
      return;
    }

    this.menuIndex = this.menuItems.length - 1;

    const lastItem = this.#getItemByIndex(this.menuIndex);
    if (lastItem && lastItem.isFocusable()) {
      this.#focusItem(lastItem);
    } else {
      this.focusPreviousItem();
    }
  }

  public focusPreviousItem(): void {
    if (!this.#hasFocusableItems()) {
      return;
    }

    this.menuIndex--;

    const previousItem = this.#getItemByIndex(this.menuIndex);
    if (previousItem && previousItem.isFocusable()) {
      this.#focusItem(previousItem);
    } else {
      this.focusPreviousItem();
    }
  }

  public focusNextItem() {
    if (!this.#hasFocusableItems()) {
      return;
    }

    this.menuIndex++;

    const nextItem = this.#getItemByIndex(this.menuIndex);
    if (nextItem && nextItem.isFocusable()) {
      this.#focusItem(nextItem);
    } else {
      this.focusNextItem();
    }
  }

  public reset(): void {
    this.#_menuIndex = -1;
    this.#resetItemsActiveState();
    this.#changeDetector.markForCheck();
  }

  #resetItemsActiveState() {
    this.menuItems.forEach((item: SkyDropdownItemComponent) => {
      item.resetState();
    });
  }

  #focusItem(item: SkyDropdownItemComponent): void {
    this.#resetItemsActiveState();
    item.focusElement(this.useNativeFocus);
    this.menuChanges.emit({
      activeIndex: this.menuIndex,
    });
  }

  #getItemByIndex(index: number): SkyDropdownItemComponent | undefined {
    return this.menuItems.find((_, i) => i === index);
  }

  #selectItemByEventTarget(target: EventTarget): void {
    const selectedItem = this.menuItems.find(
      (item: SkyDropdownItemComponent, i: number) => {
        const found = item.elementRef.nativeElement.contains(target);

        if (found) {
          this.menuIndex = i;
          this.menuChanges.next({
            activeIndex: this.menuIndex,
          });
        }

        return found;
      }
    );

    /* istanbul ignore else */
    if (selectedItem) {
      this.menuChanges.next({
        selectedItem,
      });
    }
  }

  #sendMessage(type: SkyDropdownMessageType): void {
    this.#dropdownComponent?.messageStream?.next({ type });
  }

  #addEventListeners(): void {
    const dropdownMenuElement = this.#elementRef.nativeElement;

    observableFromEvent<MouseEvent>(dropdownMenuElement, 'click')
      .pipe(takeUntil(this.#ngUnsubscribe))
      .subscribe((event) => {
        this.#selectItemByEventTarget(event.target as EventTarget);
      });

    observableFromEvent<KeyboardEvent>(dropdownMenuElement, 'keydown')
      .pipe(takeUntil(this.#ngUnsubscribe))
      .subscribe((event) => {
        const key = event.key.toLowerCase();

        switch (key) {
          case 'escape':
            this.#sendMessage(SkyDropdownMessageType.Close);
            this.#sendMessage(SkyDropdownMessageType.FocusTriggerButton);
            event.stopPropagation();
            event.preventDefault();
            break;

          case 'arrowdown':
          case 'down':
            this.focusNextItem();
            event.preventDefault();
            break;

          case 'arrowup':
          case 'up':
            this.focusPreviousItem();
            event.preventDefault();
            break;

          case 'tab':
            if (this.#dropdownComponent?.dismissOnBlur) {
              this.#sendMessage(SkyDropdownMessageType.Close);
            }
            this.#sendMessage(SkyDropdownMessageType.FocusTriggerButton);
            break;
        }
      });

    observableFromEvent(dropdownMenuElement, 'mouseenter')
      .pipe(takeUntil(this.#ngUnsubscribe))
      .subscribe(() => {
        /* istanbul ignore else */
        if (this.#dropdownComponent) {
          this.#dropdownComponent.isMouseEnter = true;
        }
      });

    observableFromEvent(dropdownMenuElement, 'mouseleave')
      .pipe(takeUntil(this.#ngUnsubscribe))
      .subscribe(() => {
        /* istanbul ignore else */
        if (this.#dropdownComponent) {
          this.#dropdownComponent.isMouseEnter = false;
          // Allow the dropdown component to set isMouseEnter before checking if the close action
          // should be taken.
          setTimeout(() => {
            if (
              this.#dropdownComponent &&
              this.#dropdownComponent.trigger === 'hover' &&
              this.#dropdownComponent.isMouseEnter === false
            ) {
              this.#sendMessage(SkyDropdownMessageType.Close);
            }
          });
        }
      });
  }

  #hasFocusableItems(): boolean {
    const found = this.menuItems.find((item) => item.isFocusable());
    return found !== undefined;
  }
}


text:
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
#include <set>
#include <sstream>

#include <regex>

#include <boost/algorithm/string.hpp>
#include <boost/thread.hpp>


text:
package org.funnylife.controller;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.funnylife.vo.QueryVO;
import org.funnylife.vo.ResultVO;
import org.funnylife.vo.TableColumnsVO;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.sql.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by cheng on 2017/7/2.
 */
@RestController
@RequestMapping("/sql")
public class SqlCmdController {

    @RequestMapping(value="/run", method= RequestMethod.POST)
    public ResultVO execut(@RequestBody QueryVO queryVO){
        ResultVO<Map<String, Object>> resultVO = new ResultVO<Map<String, Object>>();
        Connection con = null;
        Statement stmt = null;

        resultVO.setData(new HashMap<String, Object>());
        try {
            con = getConnection(queryVO.getDbName(), "root", "root");
            stmt = con.createStatement();

            String sql = queryVO.getSql();
            if("execute".equals(queryVO.getExecTtype())){
                boolean execute = stmt.execute(sql);
                resultVO.getData().put("data", execute);
                resultVO.getData().put("success", true);
            }else{
                ResultSet rs = stmt.executeQuery(sql);
                boolean isFirst = true;

                List<TableColumnsVO> cList = Lists.newArrayList();
                List<Map<String, String>> dList = Lists.newArrayList();

                while (rs.next()) {
                    if(isFirst) {
                        ResultSetMetaData metaData = rs.getMetaData();
                        metaData.getColumnType(1);
                        metaData.getColumnDisplaySize(1);
                        for (int i = 1; i <= metaData.getColumnCount(); i++) {
                            cList.add(new TableColumnsVO(metaData.getColumnName(i), metaData.getColumnTypeName(i), metaData.getColumnDisplaySize(i)));
                            isFirst = false;
                        }
                        resultVO.getData().put("columns", cList);
                    }
                    Map<String, String> row = Maps.newHashMap();
                    for(int i = 0; i<cList.size(); i++) {
                        TableColumnsVO col = cList.get(i);
                        row.put(col.getName(), rs.getString(col.getName()));
                    }
                    dList.add(row);
                }
                resultVO.getData().put("data", dList);
                resultVO.success();
            }
        }catch (Exception e){
            resultVO.fail();
            resultVO.setResult(e.getMessage());
        }finally {
            try {
                stmt.close();
            } catch (Exception e) {
            }
            try {
                con.close();
            } catch (Exception e) {
            }
        }
        return resultVO;
    }

    private Connection getConnection(String dbName, String user, String passwored) throws SQLException {
        String url = "jdbc:mysql://localhost:3306/"+dbName+"?user="+user+"&password="+passwored;
        Connection conn = DriverManager.getConnection(url);
        return conn;
    }
}