packages feed

EtaMOO-0.3.0.0: src/cbits/match.c

# include <string.h>
# include "match.h"

# define MAX_CAPTURES  10

/*
 * Structure for capturing rmatch results
 */
typedef struct {
  int ovec[MAX_CAPTURES * 2];
  int valid;
} rmatch_data_t;

/*
 * PCRE callout function used to capture rmatch results
 *
 * This function will be called by PCRE each time the (?C) pattern is
 * encountered, which we have arranged to occur at the end of each successful
 * pattern match. To find the rightmost match for rmatch, we record the match
 * found so far if it is further right and/or longer than the last found
 * match, then tell PCRE to continue matching as if the match had failed.
 *
 * This function is a no-op unless passed rmatch callout_data, allowing it to
 * be kept as the global PCRE callout function even for normal matches.
 */
static
int global_callout(pcre_callout_block *block)
{
  rmatch_data_t *rmatch = block->callout_data;

  if (!rmatch)
    return 0;  /* proceed as normal; not using rmatch */

  if (!rmatch->valid || block->start_match > rmatch->ovec[0] ||
      (block->start_match == rmatch->ovec[0] &&
       block->current_position > rmatch->ovec[1])) {
    /* make a copy of the offsets vector so the last such vector found can be
       returned as the rightmost match */
    rmatch->ovec[0] = block->start_match;
    rmatch->ovec[1] = block->current_position;
    memcpy(&rmatch->ovec[2], &block->offset_vector[2],
           sizeof(rmatch->ovec[2]) * 2 * (block->capture_top - 1));

    rmatch->valid = block->capture_top;
  }

  return 1;  /* cause match failure at current point, but continue trying */
}

/*
 * match() helper function
 *
 * Arrange to call pcre_exec() with a callout that does nothing. We use the
 * same callout function as rmatch_helper() so that we can run simultaneously
 * with it, as pcre_callout is a global variable.
 */
int match_helper(const pcre *code, const pcre_extra *extra,
                 const char *subject, int length,
                 int options, int ovector[MAX_CAPTURES * 3])
{
  /* we rely on the fact that *extra has no callout_data by default */

  pcre_callout = global_callout;

  return pcre_exec(code, extra, subject, length, 0,
		   options, ovector, MAX_CAPTURES * 3);
}

/*
 * rmatch() helper function
 *
 * Arrange to call pcre_exec() with a callout that records the rightmost
 * match. We use the same callout function as match_helper() so that we can
 * run simultaneously with it, as pcre_callout is a global variable.
 *
 * We also use an option to disable PCRE optimizations that can interfere with
 * rmatch semantics. This has a side-effect of disabling JIT unless the
 * pattern has been compiled using the same option.
 */
int rmatch_helper(const pcre *code, const pcre_extra *extra,
                  const char *subject, int length,
                  int options, int ovector[MAX_CAPTURES * 3])
{
  pcre_extra local_extra = *extra;
  rmatch_data_t rmatch;
  int rc;

  rmatch.valid = 0;

  /* modify a local copy to avoid interfering with any other threads */
  local_extra.callout_data = &rmatch;
  local_extra.flags |= PCRE_EXTRA_CALLOUT_DATA;

  pcre_callout = global_callout;

  rc = pcre_exec(code, &local_extra, subject, length, 0,
		 options | PCRE_NO_START_OPTIMIZE, ovector, MAX_CAPTURES * 3);
  if (rc == PCRE_ERROR_NOMATCH && rmatch.valid) {
    rc = rmatch.valid;
    memcpy(ovector, &rmatch.ovec[0], sizeof(ovector[0]) * 2 * rc);
  }

  return rc;
}