int main()

in pcre/pcretest.c [2983:5751]


int main(int argc, char **argv)
{
FILE *infile = stdin;
const char *version;
int options = 0;
int study_options = 0;
int default_find_match_limit = FALSE;
pcre_uint32 default_options = 0;
int op = 1;
int timeit = 0;
int timeitm = 0;
int showtotaltimes = 0;
int showinfo = 0;
int showstore = 0;
int force_study = -1;
int force_study_options = 0;
int quiet = 0;
int size_offsets = 45;
int size_offsets_max;
int *offsets = NULL;
int debug = 0;
int done = 0;
int all_use_dfa = 0;
int verify_jit = 0;
int yield = 0;
int stack_size;
pcre_uint8 *dbuffer = NULL;
pcre_uint8 lockout[24] = { 0 };
size_t dbuffer_size = 1u << 14;
clock_t total_compile_time = 0;
clock_t total_study_time = 0;
clock_t total_match_time = 0;

#if !defined NOPOSIX
int posix = 0;
#endif
#if !defined NODFA
int *dfa_workspace = NULL;
#endif

pcre_jit_stack *jit_stack = NULL;

/* These vectors store, end-to-end, a list of zero-terminated captured
substring names, each list itself being terminated by an empty name. Assume
that 1024 is plenty long enough for the few names we'll be testing. It is
easiest to keep separate 8-, 16- and 32-bit versions, using the 32-bit version
for the actual memory, to ensure alignment. */

pcre_uint32 copynames[1024];
pcre_uint32 getnames[1024];

#ifdef SUPPORT_PCRE32
pcre_uint32 *cn32ptr;
pcre_uint32 *gn32ptr;
#endif

#ifdef SUPPORT_PCRE16
pcre_uint16 *copynames16 = (pcre_uint16 *)copynames;
pcre_uint16 *getnames16 = (pcre_uint16 *)getnames;
pcre_uint16 *cn16ptr;
pcre_uint16 *gn16ptr;
#endif

#ifdef SUPPORT_PCRE8
pcre_uint8 *copynames8 = (pcre_uint8 *)copynames;
pcre_uint8 *getnames8 = (pcre_uint8 *)getnames;
pcre_uint8 *cn8ptr;
pcre_uint8 *gn8ptr;
#endif

/* Get buffers from malloc() so that valgrind will check their misuse when
debugging. They grow automatically when very long lines are read. The 16-
and 32-bit buffers (buffer16, buffer32) are obtained only if needed. */

buffer = (pcre_uint8 *)malloc(buffer_size);
pbuffer = (pcre_uint8 *)malloc(buffer_size);

/* The outfile variable is static so that new_malloc can use it. */

outfile = stdout;

/* The following  _setmode() stuff is some Windows magic that tells its runtime
library to translate CRLF into a single LF character. At least, that's what
I've been told: never having used Windows I take this all on trust. Originally
it set 0x8000, but then I was advised that _O_BINARY was better. */

#if defined(_WIN32) || defined(WIN32)
_setmode( _fileno( stdout ), _O_BINARY );
#endif

/* Get the version number: both pcre_version() and pcre16_version() give the
same answer. We just need to ensure that we call one that is available. */

#if defined SUPPORT_PCRE8
version = pcre_version();
#elif defined SUPPORT_PCRE16
version = pcre16_version();
#elif defined SUPPORT_PCRE32
version = pcre32_version();
#endif

/* Scan options */

while (argc > 1 && argv[op][0] == '-')
  {
  pcre_uint8 *endptr;
  char *arg = argv[op];

  if (strcmp(arg, "-m") == 0) showstore = 1;
  else if (strcmp(arg, "-s") == 0) force_study = 0;

  else if (strncmp(arg, "-s+", 3) == 0)
    {
    arg += 3;
    if (*arg == '+') { arg++; verify_jit = TRUE; }
    force_study = 1;
    if (*arg == 0)
      force_study_options = jit_study_bits[6];
    else if (*arg >= '1' && *arg <= '7')
      force_study_options = jit_study_bits[*arg - '1'];
    else goto BAD_ARG;
    }
  else if (strcmp(arg, "-8") == 0)
    {
#ifdef SUPPORT_PCRE8
    pcre_mode = PCRE8_MODE;
#else
    printf("** This version of PCRE was built without 8-bit support\n");
    exit(1);
#endif
    }
  else if (strcmp(arg, "-16") == 0)
    {
#ifdef SUPPORT_PCRE16
    pcre_mode = PCRE16_MODE;
#else
    printf("** This version of PCRE was built without 16-bit support\n");
    exit(1);
#endif
    }
  else if (strcmp(arg, "-32") == 0)
    {
#ifdef SUPPORT_PCRE32
    pcre_mode = PCRE32_MODE;
#else
    printf("** This version of PCRE was built without 32-bit support\n");
    exit(1);
#endif
    }
  else if (strcmp(arg, "-q") == 0) quiet = 1;
  else if (strcmp(arg, "-b") == 0) debug = 1;
  else if (strcmp(arg, "-i") == 0) showinfo = 1;
  else if (strcmp(arg, "-d") == 0) showinfo = debug = 1;
  else if (strcmp(arg, "-M") == 0) default_find_match_limit = TRUE;
  else if (strcmp(arg, "-O") == 0) default_options |= PCRE_NO_AUTO_POSSESS;
#if !defined NODFA
  else if (strcmp(arg, "-dfa") == 0) all_use_dfa = 1;
#endif
  else if (strcmp(arg, "-o") == 0 && argc > 2 &&
      ((size_offsets = get_value((pcre_uint8 *)argv[op+1], &endptr)),
        *endptr == 0))
    {
    op++;
    argc--;
    }
  else if (strcmp(arg, "-t") == 0 || strcmp(arg, "-tm") == 0 ||
           strcmp(arg, "-T") == 0 || strcmp(arg, "-TM") == 0)
    {
    int temp;
    int both = arg[2] == 0;
    showtotaltimes = arg[1] == 'T';
    if (argc > 2 && (temp = get_value((pcre_uint8 *)argv[op+1], &endptr),
                     *endptr == 0))
      {
      timeitm = temp;
      op++;
      argc--;
      }
    else timeitm = LOOPREPEAT;
    if (both) timeit = timeitm;
    }
  else if (strcmp(arg, "-S") == 0 && argc > 2 &&
      ((stack_size = get_value((pcre_uint8 *)argv[op+1], &endptr)),
        *endptr == 0))
    {
#if defined(_WIN32) || defined(WIN32) || defined(__minix) || defined(NATIVE_ZOS) || defined(__VMS)
    printf("PCRE: -S not supported on this OS\n");
    exit(1);
#else
    int rc;
    struct rlimit rlim;
    getrlimit(RLIMIT_STACK, &rlim);
    rlim.rlim_cur = stack_size * 1024 * 1024;
    rc = setrlimit(RLIMIT_STACK, &rlim);
    if (rc != 0)
      {
    printf("PCRE: setrlimit() failed with error %d\n", rc);
    exit(1);
      }
    op++;
    argc--;
#endif
    }
#if !defined NOPOSIX
  else if (strcmp(arg, "-p") == 0) posix = 1;
#endif
  else if (strcmp(arg, "-C") == 0)
    {
    int rc;
    unsigned long int lrc;

    if (argc > 2)
      {
      if (strcmp(argv[op + 1], "linksize") == 0)
        {
        (void)PCRE_CONFIG(PCRE_CONFIG_LINK_SIZE, &rc);
        printf("%d\n", rc);
        yield = rc;

#ifdef __VMS
        vms_setsymbol("LINKSIZE",0,yield );
#endif
        }
      else if (strcmp(argv[op + 1], "pcre8") == 0)
        {
#ifdef SUPPORT_PCRE8
        printf("1\n");
        yield = 1;
#else
        printf("0\n");
        yield = 0;
#endif
#ifdef __VMS
        vms_setsymbol("PCRE8",0,yield );
#endif
        }
      else if (strcmp(argv[op + 1], "pcre16") == 0)
        {
#ifdef SUPPORT_PCRE16
        printf("1\n");
        yield = 1;
#else
        printf("0\n");
        yield = 0;
#endif
#ifdef __VMS
        vms_setsymbol("PCRE16",0,yield );
#endif
        }
      else if (strcmp(argv[op + 1], "pcre32") == 0)
        {
#ifdef SUPPORT_PCRE32
        printf("1\n");
        yield = 1;
#else
        printf("0\n");
        yield = 0;
#endif
#ifdef __VMS
        vms_setsymbol("PCRE32",0,yield );
#endif
        }
      else if (strcmp(argv[op + 1], "utf") == 0)
        {
#ifdef SUPPORT_PCRE8
        if (pcre_mode == PCRE8_MODE)
          (void)pcre_config(PCRE_CONFIG_UTF8, &rc);
#endif
#ifdef SUPPORT_PCRE16
        if (pcre_mode == PCRE16_MODE)
          (void)pcre16_config(PCRE_CONFIG_UTF16, &rc);
#endif
#ifdef SUPPORT_PCRE32
        if (pcre_mode == PCRE32_MODE)
          (void)pcre32_config(PCRE_CONFIG_UTF32, &rc);
#endif
        printf("%d\n", rc);
        yield = rc;
#ifdef __VMS
        vms_setsymbol("UTF",0,yield );
#endif
        }
      else if (strcmp(argv[op + 1], "ucp") == 0)
        {
        (void)PCRE_CONFIG(PCRE_CONFIG_UNICODE_PROPERTIES, &rc);
        printf("%d\n", rc);
        yield = rc;
        }
      else if (strcmp(argv[op + 1], "jit") == 0)
        {
        (void)PCRE_CONFIG(PCRE_CONFIG_JIT, &rc);
        printf("%d\n", rc);
        yield = rc;
        }
      else if (strcmp(argv[op + 1], "newline") == 0)
        {
        (void)PCRE_CONFIG(PCRE_CONFIG_NEWLINE, &rc);
        print_newline_config(rc, TRUE);
        }
      else if (strcmp(argv[op + 1], "bsr") == 0)
        {
        (void)PCRE_CONFIG(PCRE_CONFIG_BSR, &rc);
        printf("%s\n", rc? "ANYCRLF" : "ANY");
        }
      else if (strcmp(argv[op + 1], "ebcdic") == 0)
        {
#ifdef EBCDIC
        printf("1\n");
        yield = 1;
#else
        printf("0\n");
#endif
        }
      else if (strcmp(argv[op + 1], "ebcdic-nl") == 0)
        {
#ifdef EBCDIC
        printf("0x%02x\n", CHAR_LF);
#else
        printf("0\n");
#endif
        }
      else
        {
        printf("Unknown -C option: %s\n", argv[op + 1]);
        }
      goto EXIT;
      }

    /* No argument for -C: output all configuration information. */

    printf("PCRE version %s\n", version);
    printf("Compiled with\n");

#ifdef EBCDIC
    printf("  EBCDIC code support: LF is 0x%02x\n", CHAR_LF);
#endif

/* At least one of SUPPORT_PCRE8 and SUPPORT_PCRE16 will be set. If both
are set, either both UTFs are supported or both are not supported. */

#ifdef SUPPORT_PCRE8
    printf("  8-bit support\n");
    (void)pcre_config(PCRE_CONFIG_UTF8, &rc);
      printf ("  %sUTF-8 support\n", rc ? "" : "No ");
#endif
#ifdef SUPPORT_PCRE16
    printf("  16-bit support\n");
    (void)pcre16_config(PCRE_CONFIG_UTF16, &rc);
    printf ("  %sUTF-16 support\n", rc ? "" : "No ");
#endif
#ifdef SUPPORT_PCRE32
    printf("  32-bit support\n");
    (void)pcre32_config(PCRE_CONFIG_UTF32, &rc);
    printf ("  %sUTF-32 support\n", rc ? "" : "No ");
#endif

    (void)PCRE_CONFIG(PCRE_CONFIG_UNICODE_PROPERTIES, &rc);
    printf("  %sUnicode properties support\n", rc? "" : "No ");
    (void)PCRE_CONFIG(PCRE_CONFIG_JIT, &rc);
    if (rc)
      {
      const char *arch;
      (void)PCRE_CONFIG(PCRE_CONFIG_JITTARGET, (void *)(&arch));
      printf("  Just-in-time compiler support: %s\n", arch);
      }
    else
      printf("  No just-in-time compiler support\n");
    (void)PCRE_CONFIG(PCRE_CONFIG_NEWLINE, &rc);
    print_newline_config(rc, FALSE);
    (void)PCRE_CONFIG(PCRE_CONFIG_BSR, &rc);
    printf("  \\R matches %s\n", rc? "CR, LF, or CRLF only" :
                                     "all Unicode newlines");
    (void)PCRE_CONFIG(PCRE_CONFIG_LINK_SIZE, &rc);
    printf("  Internal link size = %d\n", rc);
    (void)PCRE_CONFIG(PCRE_CONFIG_POSIX_MALLOC_THRESHOLD, &rc);
    printf("  POSIX malloc threshold = %d\n", rc);
    (void)PCRE_CONFIG(PCRE_CONFIG_PARENS_LIMIT, &lrc);
    printf("  Parentheses nest limit = %ld\n", lrc);
    (void)PCRE_CONFIG(PCRE_CONFIG_MATCH_LIMIT, &lrc);
    printf("  Default match limit = %ld\n", lrc);
    (void)PCRE_CONFIG(PCRE_CONFIG_MATCH_LIMIT_RECURSION, &lrc);
    printf("  Default recursion depth limit = %ld\n", lrc);
    (void)PCRE_CONFIG(PCRE_CONFIG_STACKRECURSE, &rc);
    printf("  Match recursion uses %s", rc? "stack" : "heap");
    if (showstore)
      {
      PCRE_EXEC(stack_size, NULL, NULL, NULL, -999, -999, 0, NULL, 0);
      printf(": %sframe size = %d bytes", rc? "approximate " : "", -stack_size);
      }
    printf("\n");
    goto EXIT;
    }
  else if (strcmp(arg, "-help") == 0 ||
           strcmp(arg, "--help") == 0)
    {
    usage();
    goto EXIT;
    }
  else
    {
    BAD_ARG:
    printf("** Unknown or malformed option %s\n", arg);
    usage();
    yield = 1;
    goto EXIT;
    }
  op++;
  argc--;
  }

/* Get the store for the offsets vector, and remember what it was */

size_offsets_max = size_offsets;
offsets = (int *)malloc(size_offsets_max * sizeof(int));
if (offsets == NULL)
  {
  printf("** Failed to get %d bytes of memory for offsets vector\n",
    (int)(size_offsets_max * sizeof(int)));
  yield = 1;
  goto EXIT;
  }

/* Sort out the input and output files */

if (argc > 1)
  {
  infile = fopen(argv[op], INPUT_MODE);
  if (infile == NULL)
    {
    printf("** Failed to open %s\n", argv[op]);
    yield = 1;
    goto EXIT;
    }
  }

if (argc > 2)
  {
  outfile = fopen(argv[op+1], OUTPUT_MODE);
  if (outfile == NULL)
    {
    printf("** Failed to open %s\n", argv[op+1]);
    yield = 1;
    goto EXIT;
    }
  }

/* Set alternative malloc function */

#ifdef SUPPORT_PCRE8
pcre_malloc = new_malloc;
pcre_free = new_free;
pcre_stack_malloc = stack_malloc;
pcre_stack_free = stack_free;
#endif

#ifdef SUPPORT_PCRE16
pcre16_malloc = new_malloc;
pcre16_free = new_free;
pcre16_stack_malloc = stack_malloc;
pcre16_stack_free = stack_free;
#endif

#ifdef SUPPORT_PCRE32
pcre32_malloc = new_malloc;
pcre32_free = new_free;
pcre32_stack_malloc = stack_malloc;
pcre32_stack_free = stack_free;
#endif

/* Heading line unless quiet */

if (!quiet) fprintf(outfile, "PCRE version %s\n\n", version);

/* Main loop */

while (!done)
  {
  pcre *re = NULL;
  pcre_extra *extra = NULL;

#if !defined NOPOSIX  /* There are still compilers that require no indent */
  regex_t preg = { NULL, 0, 0} ;
  int do_posix = 0;
#endif

  const char *error;
  pcre_uint8 *markptr;
  pcre_uint8 *p, *pp, *ppp;
  pcre_uint8 *to_file = NULL;
  const pcre_uint8 *tables = NULL;
  unsigned long int get_options;
  unsigned long int true_size, true_study_size = 0;
  size_t size;
  int do_allcaps = 0;
  int do_mark = 0;
  int do_study = 0;
  int no_force_study = 0;
  int do_debug = debug;
  int do_G = 0;
  int do_g = 0;
  int do_showinfo = showinfo;
  int do_showrest = 0;
  int do_showcaprest = 0;
  int do_flip = 0;
  int erroroffset, len, delimiter, poffset;

#if !defined NODFA
  int dfa_matched = 0;
#endif

  use_utf = 0;
  debug_lengths = 1;
  SET_PCRE_STACK_GUARD(NULL);

  if (extend_inputline(infile, buffer, "  re> ") == NULL) break;
  if (infile != stdin) fprintf(outfile, "%s", (char *)buffer);
  fflush(outfile);

  p = buffer;
  while (isspace(*p)) p++;
  if (*p == 0) continue;

  /* Handle option lock-out setting */

  if (*p == '<' && p[1] == ' ')
    {
    p += 2;
    while (isspace(*p)) p++;
    if (strncmp((char *)p, "forbid ", 7) == 0)
      {
      p += 7;
      while (isspace(*p)) p++;
      pp = lockout;
      while (!isspace(*p) && pp < lockout + sizeof(lockout) - 1)
        *pp++ = *p++;
      *pp = 0;
      }
    else
      {
      printf("** Unrecognized special command '%s'\n", p);
      yield = 1;
      goto EXIT;
      }
    continue;
    }

  /* See if the pattern is to be loaded pre-compiled from a file. */

  if (*p == '<' && strchr((char *)(p+1), '<') == NULL)
    {
    pcre_uint32 magic;
    pcre_uint8 sbuf[8];
    FILE *f;

    p++;
    if (*p == '!')
      {
      do_debug = TRUE;
      do_showinfo = TRUE;
      p++;
      }

    pp = p + (int)strlen((char *)p);
    while (isspace(pp[-1])) pp--;
    *pp = 0;

    f = fopen((char *)p, "rb");
    if (f == NULL)
      {
      fprintf(outfile, "Failed to open %s: %s\n", p, strerror(errno));
      continue;
      }
    if (fread(sbuf, 1, 8, f) != 8) goto FAIL_READ;

    true_size =
      (sbuf[0] << 24) | (sbuf[1] << 16) | (sbuf[2] << 8) | sbuf[3];
    true_study_size =
      (sbuf[4] << 24) | (sbuf[5] << 16) | (sbuf[6] << 8) | sbuf[7];

    re = (pcre *)new_malloc(true_size);
    if (re == NULL)
      {
      printf("** Failed to get %d bytes of memory for pcre object\n",
        (int)true_size);
      yield = 1;
      goto EXIT;
      }
    if (fread(re, 1, true_size, f) != true_size) goto FAIL_READ;

    magic = REAL_PCRE_MAGIC(re);
    if (magic != MAGIC_NUMBER)
      {
      if (swap_uint32(magic) == MAGIC_NUMBER)
        {
        do_flip = 1;
        }
      else
        {
        fprintf(outfile, "Data in %s is not a compiled PCRE regex\n", p);
        new_free(re);
        fclose(f);
        continue;
        }
      }

    /* We hide the byte-invert info for little and big endian tests. */
    fprintf(outfile, "Compiled pattern%s loaded from %s\n",
      do_flip && (p[-1] == '<') ? " (byte-inverted)" : "", p);

    /* Now see if there is any following study data. */

    if (true_study_size != 0)
      {
      pcre_study_data *psd;

      extra = (pcre_extra *)new_malloc(sizeof(pcre_extra) + true_study_size);
      extra->flags = PCRE_EXTRA_STUDY_DATA;

      psd = (pcre_study_data *)(((char *)extra) + sizeof(pcre_extra));
      extra->study_data = psd;

      if (fread(psd, 1, true_study_size, f) != true_study_size)
        {
        FAIL_READ:
        fprintf(outfile, "Failed to read data from %s\n", p);
        if (extra != NULL)
          {
          PCRE_FREE_STUDY(extra);
          }
        new_free(re);
        fclose(f);
        continue;
        }
      fprintf(outfile, "Study data loaded from %s\n", p);
      do_study = 1;     /* To get the data output if requested */
      }
    else fprintf(outfile, "No study data\n");

    /* Flip the necessary bytes. */
    if (do_flip)
      {
      int rc;
      PCRE_PATTERN_TO_HOST_BYTE_ORDER(rc, re, extra, NULL);
      if (rc == PCRE_ERROR_BADMODE)
        {
        pcre_uint32 flags_in_host_byte_order;
        if (REAL_PCRE_MAGIC(re) == MAGIC_NUMBER)
          flags_in_host_byte_order = REAL_PCRE_FLAGS(re);
        else
          flags_in_host_byte_order = swap_uint32(REAL_PCRE_FLAGS(re));
        /* Simulate the result of the function call below. */
        fprintf(outfile, "Error %d from pcre%s_fullinfo(%d)\n", rc,
          pcre_mode == PCRE32_MODE ? "32" : pcre_mode == PCRE16_MODE ? "16" : "",
          PCRE_INFO_OPTIONS);
        fprintf(outfile, "Running in %d-bit mode but pattern was compiled in "
          "%d-bit mode\n", 8 * CHAR_SIZE, 8 * (flags_in_host_byte_order & PCRE_MODE_MASK));
        new_free(re);
        fclose(f);
        continue;
        }
      }

    /* Need to know if UTF-8 for printing data strings. */

    if (new_info(re, NULL, PCRE_INFO_OPTIONS, &get_options) < 0)
      {
      new_free(re);
      fclose(f);
      continue;
      }
    use_utf = (get_options & PCRE_UTF8) != 0;

    fclose(f);
    goto SHOW_INFO;
    }

  /* In-line pattern (the usual case). Get the delimiter and seek the end of
  the pattern; if it isn't complete, read more. */

  delimiter = *p++;

  if (isalnum(delimiter) || delimiter == '\\')
    {
    fprintf(outfile, "** Delimiter must not be alphanumeric or \\\n");
    goto SKIP_DATA;
    }

  pp = p;
  poffset = (int)(p - buffer);

  for(;;)
    {
    while (*pp != 0)
      {
      if (*pp == '\\' && pp[1] != 0) pp++;
        else if (*pp == delimiter) break;
      pp++;
      }
    if (*pp != 0) break;
    if ((pp = extend_inputline(infile, pp, "    > ")) == NULL)
      {
      fprintf(outfile, "** Unexpected EOF\n");
      done = 1;
      goto CONTINUE;
      }
    if (infile != stdin) fprintf(outfile, "%s", (char *)pp);
    }

  /* The buffer may have moved while being extended; reset the start of data
  pointer to the correct relative point in the buffer. */

  p = buffer + poffset;

  /* If the first character after the delimiter is backslash, make
  the pattern end with backslash. This is purely to provide a way
  of testing for the error message when a pattern ends with backslash. */

  if (pp[1] == '\\') *pp++ = '\\';

  /* Terminate the pattern at the delimiter, and save a copy of the pattern
  for callouts. */

  *pp++ = 0;
  strcpy((char *)pbuffer, (char *)p);

  /* Look for modifiers and options after the final delimiter. */

  options = default_options;
  study_options = force_study_options;
  log_store = showstore;  /* default from command line */

  while (*pp != 0)
    {
    /* Check to see whether this modifier has been locked out for this file.
    This is complicated for the multi-character options that begin with '<'.
    If there is no '>' in the lockout string, all multi-character modifiers are
    locked out. */

    if (strchr((char *)lockout, *pp) != NULL)
      {
      if (*pp == '<' && strchr((char *)lockout, '>') != NULL)
        {
        int x = check_mc_option(pp+1, outfile, FALSE, "modifier");
        if (x == 0) goto SKIP_DATA;

        for (ppp = lockout; *ppp != 0; ppp++)
          {
          if (*ppp == '<')
            {
            int y = check_mc_option(ppp+1, outfile, FALSE, "modifier");
            if (y == 0)
              {
              printf("** Error in modifier forbid data - giving up.\n");
              yield = 1;
              goto EXIT;
              }
            if (x == y)
              {
              ppp = pp;
              while (*ppp != '>') ppp++;
              printf("** The %.*s modifier is locked out - giving up.\n",
                (int)(ppp - pp + 1), pp);
              yield = 1;
              goto EXIT;
              }
            }
          }
        }

      /* The single-character modifiers are straightforward. */

      else
        {
        printf("** The /%c modifier is locked out - giving up.\n", *pp);
        yield = 1;
        goto EXIT;
        }
      }

    /* The modifier is not locked out; handle it. */

    switch (*pp++)
      {
      case 'f': options |= PCRE_FIRSTLINE; break;
      case 'g': do_g = 1; break;
      case 'i': options |= PCRE_CASELESS; break;
      case 'm': options |= PCRE_MULTILINE; break;
      case 's': options |= PCRE_DOTALL; break;
      case 'x': options |= PCRE_EXTENDED; break;

      case '+':
      if (do_showrest) do_showcaprest = 1; else do_showrest = 1;
      break;

      case '=': do_allcaps = 1; break;
      case 'A': options |= PCRE_ANCHORED; break;
      case 'B': do_debug = 1; break;
      case 'C': options |= PCRE_AUTO_CALLOUT; break;
      case 'D': do_debug = do_showinfo = 1; break;
      case 'E': options |= PCRE_DOLLAR_ENDONLY; break;
      case 'F': do_flip = 1; break;
      case 'G': do_G = 1; break;
      case 'I': do_showinfo = 1; break;
      case 'J': options |= PCRE_DUPNAMES; break;
      case 'K': do_mark = 1; break;
      case 'M': log_store = 1; break;
      case 'N': options |= PCRE_NO_AUTO_CAPTURE; break;
      case 'O': options |= PCRE_NO_AUTO_POSSESS; break;

#if !defined NOPOSIX
      case 'P': do_posix = 1; break;
#endif

      case 'Q':
      switch (*pp)
        {
        case '0':
        case '1':
        stack_guard_return = *pp++ - '0';
        break;

        default:
        fprintf(outfile, "** Missing 0 or 1 after /Q\n");
        goto SKIP_DATA;
        }
      SET_PCRE_STACK_GUARD(stack_guard);
      break;

      case 'S':
      do_study = 1;
      for (;;)
        {
        switch (*pp++)
          {
          case 'S':
          do_study = 0;
          no_force_study = 1;
          break;

          case '!':
          study_options |= PCRE_STUDY_EXTRA_NEEDED;
          break;

          case '+':
          if (*pp == '+')
            {
            verify_jit = TRUE;
            pp++;
            }
          if (*pp >= '1' && *pp <= '7')
            study_options |= jit_study_bits[*pp++ - '1'];
          else
            study_options |= jit_study_bits[6];
          break;

          case '-':
          study_options &= ~PCRE_STUDY_ALLJIT;
          break;

          default:
          pp--;
          goto ENDLOOP;
          }
        }
      ENDLOOP:
      break;

      case 'U': options |= PCRE_UNGREEDY; break;
      case 'W': options |= PCRE_UCP; break;
      case 'X': options |= PCRE_EXTRA; break;
      case 'Y': options |= PCRE_NO_START_OPTIMISE; break;
      case 'Z': debug_lengths = 0; break;
      case '8': options |= PCRE_UTF8; use_utf = 1; break;
      case '9': options |= PCRE_NEVER_UTF; break;
      case '?': options |= PCRE_NO_UTF8_CHECK; break;

      case 'T':
      switch (*pp++)
        {
        case '0': tables = tables0; break;
        case '1': tables = tables1; break;

        case '\r':
        case '\n':
        case ' ':
        case 0:
        fprintf(outfile, "** Missing table number after /T\n");
        goto SKIP_DATA;

        default:
        fprintf(outfile, "** Bad table number \"%c\" after /T\n", pp[-1]);
        goto SKIP_DATA;
        }
      break;

      case 'L':
      ppp = pp;
      /* The '\r' test here is so that it works on Windows. */
      /* The '0' test is just in case this is an unterminated line. */
      while (*ppp != 0 && *ppp != '\n' && *ppp != '\r' && *ppp != ' ') ppp++;
      *ppp = 0;
      if (setlocale(LC_CTYPE, (const char *)pp) == NULL)
        {
        fprintf(outfile, "** Failed to set locale \"%s\"\n", pp);
        goto SKIP_DATA;
        }
      locale_set = 1;
      tables = PCRE_MAKETABLES;
      pp = ppp;
      break;

      case '>':
      to_file = pp;
      while (*pp != 0) pp++;
      while (isspace(pp[-1])) pp--;
      *pp = 0;
      break;

      case '<':
        {
        int x = check_mc_option(pp, outfile, FALSE, "modifier");
        if (x == 0) goto SKIP_DATA;
        options |= x;
        while (*pp++ != '>');
        }
      break;

      case '\r':                      /* So that it works in Windows */
      case '\n':
      case ' ':
      break;

      default:
      fprintf(outfile, "** Unknown modifier '%c'\n", pp[-1]);
      goto SKIP_DATA;
      }
    }

  /* Handle compiling via the POSIX interface, which doesn't support the
  timing, showing, or debugging options, nor the ability to pass over
  local character tables. Neither does it have 16-bit support. */

#if !defined NOPOSIX
  if (posix || do_posix)
    {
    int rc;
    int cflags = 0;

    if ((options & PCRE_CASELESS) != 0) cflags |= REG_ICASE;
    if ((options & PCRE_MULTILINE) != 0) cflags |= REG_NEWLINE;
    if ((options & PCRE_DOTALL) != 0) cflags |= REG_DOTALL;
    if ((options & PCRE_NO_AUTO_CAPTURE) != 0) cflags |= REG_NOSUB;
    if ((options & PCRE_UTF8) != 0) cflags |= REG_UTF8;
    if ((options & PCRE_UCP) != 0) cflags |= REG_UCP;
    if ((options & PCRE_UNGREEDY) != 0) cflags |= REG_UNGREEDY;

    rc = regcomp(&preg, (char *)p, cflags);

    /* Compilation failed; go back for another re, skipping to blank line
    if non-interactive. */

    if (rc != 0)
      {
      (void)regerror(rc, &preg, (char *)buffer, buffer_size);
      fprintf(outfile, "Failed: POSIX code %d: %s\n", rc, buffer);
      goto SKIP_DATA;
      }
    }

  /* Handle compiling via the native interface */

  else
#endif  /* !defined NOPOSIX */

    {
    /* In 16- or 32-bit mode, convert the input. */

#ifdef SUPPORT_PCRE16
    if (pcre_mode == PCRE16_MODE)
      {
      switch(to16(FALSE, p, options & PCRE_UTF8, (int)strlen((char *)p)))
        {
        case -1:
        fprintf(outfile, "**Failed: invalid UTF-8 string cannot be "
          "converted to UTF-16\n");
        goto SKIP_DATA;

        case -2:
        fprintf(outfile, "**Failed: character value greater than 0x10ffff "
          "cannot be converted to UTF-16\n");
        goto SKIP_DATA;

        case -3: /* "Impossible error" when to16 is called arg1 FALSE */
        fprintf(outfile, "**Failed: character value greater than 0xffff "
          "cannot be converted to 16-bit in non-UTF mode\n");
        goto SKIP_DATA;

        default:
        break;
        }
      p = (pcre_uint8 *)buffer16;
      }
#endif

#ifdef SUPPORT_PCRE32
    if (pcre_mode == PCRE32_MODE)
      {
      switch(to32(FALSE, p, options & PCRE_UTF32, (int)strlen((char *)p)))
        {
        case -1:
        fprintf(outfile, "**Failed: invalid UTF-8 string cannot be "
          "converted to UTF-32\n");
        goto SKIP_DATA;

        case -2:
        fprintf(outfile, "**Failed: character value greater than 0x10ffff "
          "cannot be converted to UTF-32\n");
        goto SKIP_DATA;

        case -3:
        fprintf(outfile, "**Failed: character value is ill-formed UTF-32\n");
        goto SKIP_DATA;

        default:
        break;
        }
      p = (pcre_uint8 *)buffer32;
      }
#endif

    /* Compile many times when timing */

    if (timeit > 0)
      {
      register int i;
      clock_t time_taken;
      clock_t start_time = clock();
      for (i = 0; i < timeit; i++)
        {
        PCRE_COMPILE(re, p, options, &error, &erroroffset, tables);
        if (re != NULL) free(re);
        }
      total_compile_time += (time_taken = clock() - start_time);
      fprintf(outfile, "Compile time %.4f milliseconds\n",
        (((double)time_taken * 1000.0) / (double)timeit) /
          (double)CLOCKS_PER_SEC);
      }

    PCRE_COMPILE(re, p, options, &error, &erroroffset, tables);

    /* Compilation failed; go back for another re, skipping to blank line
    if non-interactive. */

    if (re == NULL)
      {
      fprintf(outfile, "Failed: %s at offset %d\n", error, erroroffset);
      SKIP_DATA:
      if (infile != stdin)
        {
        for (;;)
          {
          if (extend_inputline(infile, buffer, NULL) == NULL)
            {
            done = 1;
            goto CONTINUE;
            }
          len = (int)strlen((char *)buffer);
          while (len > 0 && isspace(buffer[len-1])) len--;
          if (len == 0) break;
          }
        fprintf(outfile, "\n");
        }
      goto CONTINUE;
      }

    /* Compilation succeeded. It is now possible to set the UTF-8 option from
    within the regex; check for this so that we know how to process the data
    lines. */

    if (new_info(re, NULL, PCRE_INFO_OPTIONS, &get_options) < 0)
      goto SKIP_DATA;
    if ((get_options & PCRE_UTF8) != 0) use_utf = 1;

    /* Extract the size for possible writing before possibly flipping it,
    and remember the store that was got. */

    true_size = REAL_PCRE_SIZE(re);

    /* Output code size information if requested */

    if (log_store)
      {
      int name_count, name_entry_size, real_pcre_size;

      new_info(re, NULL, PCRE_INFO_NAMECOUNT, &name_count);
      new_info(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &name_entry_size);
      real_pcre_size = 0;
#ifdef SUPPORT_PCRE8
      if (REAL_PCRE_FLAGS(re) & PCRE_MODE8)
        real_pcre_size = sizeof(real_pcre);
#endif
#ifdef SUPPORT_PCRE16
      if (REAL_PCRE_FLAGS(re) & PCRE_MODE16)
        real_pcre_size = sizeof(real_pcre16);
#endif
#ifdef SUPPORT_PCRE32
      if (REAL_PCRE_FLAGS(re) & PCRE_MODE32)
        real_pcre_size = sizeof(real_pcre32);
#endif
      new_info(re, NULL, PCRE_INFO_SIZE, &size);
      fprintf(outfile, "Memory allocation (code space): %d\n",
        (int)(size - real_pcre_size - name_count * name_entry_size));
      }

    /* If -s or /S was present, study the regex to generate additional info to
    help with the matching, unless the pattern has the SS option, which
    suppresses the effect of /S (used for a few test patterns where studying is
    never sensible). */

    if (do_study || (force_study >= 0 && !no_force_study))
      {
      if (timeit > 0)
        {
        register int i;
        clock_t time_taken;
        clock_t start_time = clock();
        for (i = 0; i < timeit; i++)
          {
          PCRE_STUDY(extra, re, study_options, &error);
          }
        total_study_time = (time_taken = clock() - start_time);
        if (extra != NULL)
          {
          PCRE_FREE_STUDY(extra);
          }
        fprintf(outfile, "  Study time %.4f milliseconds\n",
          (((double)time_taken * 1000.0) / (double)timeit) /
            (double)CLOCKS_PER_SEC);
        }
      PCRE_STUDY(extra, re, study_options, &error);
      if (error != NULL)
        fprintf(outfile, "Failed to study: %s\n", error);
      else if (extra != NULL)
        {
        true_study_size = ((pcre_study_data *)(extra->study_data))->size;
        if (log_store)
          {
          size_t jitsize;
          if (new_info(re, extra, PCRE_INFO_JITSIZE, &jitsize) == 0 &&
              jitsize != 0)
            fprintf(outfile, "Memory allocation (JIT code): %d\n", (int)jitsize);
          }
        }
      }

    /* If /K was present, we set up for handling MARK data. */

    if (do_mark)
      {
      if (extra == NULL)
        {
        extra = (pcre_extra *)malloc(sizeof(pcre_extra));
        extra->flags = 0;
        }
      extra->mark = &markptr;
      extra->flags |= PCRE_EXTRA_MARK;
      }

    /* Extract and display information from the compiled data if required. */

    SHOW_INFO:

    if (do_debug)
      {
      fprintf(outfile, "------------------------------------------------------------------\n");
      PCRE_PRINTINT(re, outfile, debug_lengths);
      }

    /* We already have the options in get_options (see above) */

    if (do_showinfo)
      {
      unsigned long int all_options;
      pcre_uint32 first_char, need_char;
      pcre_uint32 match_limit, recursion_limit;
      int count, backrefmax, first_char_set, need_char_set, okpartial, jchanged,
        hascrorlf, maxlookbehind, match_empty;
      int nameentrysize, namecount;
      const pcre_uint8 *nametable;

      if (new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count) +
          new_info(re, NULL, PCRE_INFO_BACKREFMAX, &backrefmax) +
          new_info(re, NULL, PCRE_INFO_FIRSTCHARACTER, &first_char) +
          new_info(re, NULL, PCRE_INFO_FIRSTCHARACTERFLAGS, &first_char_set) +
          new_info(re, NULL, PCRE_INFO_REQUIREDCHAR, &need_char) +
          new_info(re, NULL, PCRE_INFO_REQUIREDCHARFLAGS, &need_char_set) +
          new_info(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &nameentrysize) +
          new_info(re, NULL, PCRE_INFO_NAMECOUNT, &namecount) +
          new_info(re, NULL, PCRE_INFO_NAMETABLE, (void *)&nametable) +
          new_info(re, NULL, PCRE_INFO_OKPARTIAL, &okpartial) +
          new_info(re, NULL, PCRE_INFO_JCHANGED, &jchanged) +
          new_info(re, NULL, PCRE_INFO_HASCRORLF, &hascrorlf) +
          new_info(re, NULL, PCRE_INFO_MATCH_EMPTY, &match_empty) +
          new_info(re, NULL, PCRE_INFO_MAXLOOKBEHIND, &maxlookbehind)
          != 0)
        goto SKIP_DATA;

      fprintf(outfile, "Capturing subpattern count = %d\n", count);

      if (backrefmax > 0)
        fprintf(outfile, "Max back reference = %d\n", backrefmax);

      if (maxlookbehind > 0)
        fprintf(outfile, "Max lookbehind = %d\n", maxlookbehind);

      if (new_info(re, NULL, PCRE_INFO_MATCHLIMIT, &match_limit) == 0)
        fprintf(outfile, "Match limit = %u\n", match_limit);

      if (new_info(re, NULL, PCRE_INFO_RECURSIONLIMIT, &recursion_limit) == 0)
        fprintf(outfile, "Recursion limit = %u\n", recursion_limit);

      if (namecount > 0)
        {
        fprintf(outfile, "Named capturing subpatterns:\n");
        while (namecount-- > 0)
          {
          int imm2_size = pcre_mode == PCRE8_MODE ? 2 : 1;
          int length = (int)STRLEN(nametable + imm2_size);
          fprintf(outfile, "  ");
          PCHARSV(nametable, imm2_size, length, outfile);
          while (length++ < nameentrysize - imm2_size) putc(' ', outfile);
#ifdef SUPPORT_PCRE32
          if (pcre_mode == PCRE32_MODE)
            fprintf(outfile, "%3d\n", (int)(((PCRE_SPTR32)nametable)[0]));
#endif
#ifdef SUPPORT_PCRE16
          if (pcre_mode == PCRE16_MODE)
            fprintf(outfile, "%3d\n", (int)(((PCRE_SPTR16)nametable)[0]));
#endif
#ifdef SUPPORT_PCRE8
          if (pcre_mode == PCRE8_MODE)
            fprintf(outfile, "%3d\n", ((int)nametable[0] << 8) | (int)nametable[1]);
#endif
          nametable += nameentrysize * CHAR_SIZE;
          }
        }

      if (!okpartial)  fprintf(outfile, "Partial matching not supported\n");
      if (hascrorlf)   fprintf(outfile, "Contains explicit CR or LF match\n");
      if (match_empty) fprintf(outfile, "May match empty string\n");

      all_options = REAL_PCRE_OPTIONS(re);
      if (do_flip) all_options = swap_uint32(all_options);

      if (get_options == 0) fprintf(outfile, "No options\n");
        else fprintf(outfile, "Options:%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n",
          ((get_options & PCRE_ANCHORED) != 0)? " anchored" : "",
          ((get_options & PCRE_CASELESS) != 0)? " caseless" : "",
          ((get_options & PCRE_EXTENDED) != 0)? " extended" : "",
          ((get_options & PCRE_MULTILINE) != 0)? " multiline" : "",
          ((get_options & PCRE_FIRSTLINE) != 0)? " firstline" : "",
          ((get_options & PCRE_DOTALL) != 0)? " dotall" : "",
          ((get_options & PCRE_BSR_ANYCRLF) != 0)? " bsr_anycrlf" : "",
          ((get_options & PCRE_BSR_UNICODE) != 0)? " bsr_unicode" : "",
          ((get_options & PCRE_DOLLAR_ENDONLY) != 0)? " dollar_endonly" : "",
          ((get_options & PCRE_EXTRA) != 0)? " extra" : "",
          ((get_options & PCRE_UNGREEDY) != 0)? " ungreedy" : "",
          ((get_options & PCRE_NO_AUTO_CAPTURE) != 0)? " no_auto_capture" : "",
          ((get_options & PCRE_NO_AUTO_POSSESS) != 0)? " no_auto_possessify" : "",
          ((get_options & PCRE_UTF8) != 0)? " utf" : "",
          ((get_options & PCRE_UCP) != 0)? " ucp" : "",
          ((get_options & PCRE_NO_UTF8_CHECK) != 0)? " no_utf_check" : "",
          ((get_options & PCRE_NO_START_OPTIMIZE) != 0)? " no_start_optimize" : "",
          ((get_options & PCRE_DUPNAMES) != 0)? " dupnames" : "",
          ((get_options & PCRE_NEVER_UTF) != 0)? " never_utf" : "");

      if (jchanged) fprintf(outfile, "Duplicate name status changes\n");

      switch (get_options & PCRE_NEWLINE_BITS)
        {
        case PCRE_NEWLINE_CR:
        fprintf(outfile, "Forced newline sequence: CR\n");
        break;

        case PCRE_NEWLINE_LF:
        fprintf(outfile, "Forced newline sequence: LF\n");
        break;

        case PCRE_NEWLINE_CRLF:
        fprintf(outfile, "Forced newline sequence: CRLF\n");
        break;

        case PCRE_NEWLINE_ANYCRLF:
        fprintf(outfile, "Forced newline sequence: ANYCRLF\n");
        break;

        case PCRE_NEWLINE_ANY:
        fprintf(outfile, "Forced newline sequence: ANY\n");
        break;

        default:
        break;
        }

      if (first_char_set == 2)
        {
        fprintf(outfile, "First char at start or follows newline\n");
        }
      else if (first_char_set == 1)
        {
        const char *caseless =
          ((REAL_PCRE_FLAGS(re) & PCRE_FCH_CASELESS) == 0)?
          "" : " (caseless)";

        if (PRINTOK(first_char))
          fprintf(outfile, "First char = \'%c\'%s\n", first_char, caseless);
        else
          {
          fprintf(outfile, "First char = ");
          pchar(first_char, outfile);
          fprintf(outfile, "%s\n", caseless);
          }
        }
      else
        {
        fprintf(outfile, "No first char\n");
        }

      if (need_char_set == 0)
        {
        fprintf(outfile, "No need char\n");
        }
      else
        {
        const char *caseless =
          ((REAL_PCRE_FLAGS(re) & PCRE_RCH_CASELESS) == 0)?
          "" : " (caseless)";

        if (PRINTOK(need_char))
          fprintf(outfile, "Need char = \'%c\'%s\n", need_char, caseless);
        else
          {
          fprintf(outfile, "Need char = ");
          pchar(need_char, outfile);
          fprintf(outfile, "%s\n", caseless);
          }
        }

      /* Don't output study size; at present it is in any case a fixed
      value, but it varies, depending on the computer architecture, and
      so messes up the test suite. (And with the /F option, it might be
      flipped.) If study was forced by an external -s, don't show this
      information unless -i or -d was also present. This means that, except
      when auto-callouts are involved, the output from runs with and without
      -s should be identical. */

      if (do_study || (force_study >= 0 && showinfo && !no_force_study))
        {
        if (extra == NULL)
          fprintf(outfile, "Study returned NULL\n");
        else
          {
          pcre_uint8 *start_bits = NULL;
          int minlength;

          if (new_info(re, extra, PCRE_INFO_MINLENGTH, &minlength) == 0)
            fprintf(outfile, "Subject length lower bound = %d\n", minlength);

          if (new_info(re, extra, PCRE_INFO_FIRSTTABLE, &start_bits) == 0)
            {
            if (start_bits == NULL)
              fprintf(outfile, "No starting char list\n");
            else
              {
              int i;
              int c = 24;
              fprintf(outfile, "Starting chars: ");
              for (i = 0; i < 256; i++)
                {
                if ((start_bits[i/8] & (1<<(i&7))) != 0)
                  {
                  if (c > 75)
                    {
                    fprintf(outfile, "\n  ");
                    c = 2;
                    }
                  if (PRINTOK(i) && i != ' ')
                    {
                    fprintf(outfile, "%c ", i);
                    c += 2;
                    }
                  else
                    {
                    fprintf(outfile, "\\x%02x ", i);
                    c += 5;
                    }
                  }
                }
              fprintf(outfile, "\n");
              }
            }
          }

        /* Show this only if the JIT was set by /S, not by -s. */

        if ((study_options & PCRE_STUDY_ALLJIT) != 0 &&
            (force_study_options & PCRE_STUDY_ALLJIT) == 0)
          {
          int jit;
          if (new_info(re, extra, PCRE_INFO_JIT, &jit) == 0)
            {
            if (jit)
              fprintf(outfile, "JIT study was successful\n");
            else
#ifdef SUPPORT_JIT
              fprintf(outfile, "JIT study was not successful\n");
#else
              fprintf(outfile, "JIT support is not available in this version of PCRE\n");
#endif
            }
          }
        }
      }

    /* If the '>' option was present, we write out the regex to a file, and
    that is all. The first 8 bytes of the file are the regex length and then
    the study length, in big-endian order. */

    if (to_file != NULL)
      {
      FILE *f = fopen((char *)to_file, "wb");
      if (f == NULL)
        {
        fprintf(outfile, "Unable to open %s: %s\n", to_file, strerror(errno));
        }
      else
        {
        pcre_uint8 sbuf[8];

        if (do_flip) regexflip(re, extra);
        sbuf[0] = (pcre_uint8)((true_size >> 24) & 255);
        sbuf[1] = (pcre_uint8)((true_size >> 16) & 255);
        sbuf[2] = (pcre_uint8)((true_size >>  8) & 255);
        sbuf[3] = (pcre_uint8)((true_size) & 255);
        sbuf[4] = (pcre_uint8)((true_study_size >> 24) & 255);
        sbuf[5] = (pcre_uint8)((true_study_size >> 16) & 255);
        sbuf[6] = (pcre_uint8)((true_study_size >>  8) & 255);
        sbuf[7] = (pcre_uint8)((true_study_size) & 255);

        if (fwrite(sbuf, 1, 8, f) < 8 ||
            fwrite(re, 1, true_size, f) < true_size)
          {
          fprintf(outfile, "Write error on %s: %s\n", to_file, strerror(errno));
          }
        else
          {
          fprintf(outfile, "Compiled pattern written to %s\n", to_file);

          /* If there is study data, write it. */

          if (extra != NULL)
            {
            if (fwrite(extra->study_data, 1, true_study_size, f) <
                true_study_size)
              {
              fprintf(outfile, "Write error on %s: %s\n", to_file,
                strerror(errno));
              }
            else fprintf(outfile, "Study data written to %s\n", to_file);
            }
          }
        fclose(f);
        }

      new_free(re);
      if (extra != NULL)
        {
        PCRE_FREE_STUDY(extra);
        }
      if (locale_set)
        {
        new_free((void *)tables);
        setlocale(LC_CTYPE, "C");
        locale_set = 0;
        }
      continue;  /* With next regex */
      }
    }        /* End of non-POSIX compile */

  /* Read data lines and test them */

  for (;;)
    {
#ifdef SUPPORT_PCRE8
    pcre_uint8 *q8;
#endif
#ifdef SUPPORT_PCRE16
    pcre_uint16 *q16;
#endif
#ifdef SUPPORT_PCRE32
    pcre_uint32 *q32;
#endif
    pcre_uint8 *bptr;
    int *use_offsets = offsets;
    int use_size_offsets = size_offsets;
    int callout_data = 0;
    int callout_data_set = 0;
    int count;
    pcre_uint32 c;
    int copystrings = 0;
    int find_match_limit = default_find_match_limit;
    int getstrings = 0;
    int getlist = 0;
    int gmatched = 0;
    int start_offset = 0;
    int start_offset_sign = 1;
    int g_notempty = 0;
    int use_dfa = 0;

    *copynames = 0;
    *getnames = 0;

#ifdef SUPPORT_PCRE32
    cn32ptr = copynames;
    gn32ptr = getnames;
#endif
#ifdef SUPPORT_PCRE16
    cn16ptr = copynames16;
    gn16ptr = getnames16;
#endif
#ifdef SUPPORT_PCRE8
    cn8ptr = copynames8;
    gn8ptr = getnames8;
#endif

    SET_PCRE_CALLOUT(callout);
    first_callout = 1;
    last_callout_mark = NULL;
    callout_extra = 0;
    callout_count = 0;
    callout_fail_count = 999999;
    callout_fail_id = -1;
    show_malloc = 0;
    options = 0;

    if (extra != NULL) extra->flags &=
      ~(PCRE_EXTRA_MATCH_LIMIT|PCRE_EXTRA_MATCH_LIMIT_RECURSION);

    len = 0;
    for (;;)
      {
      if (extend_inputline(infile, buffer + len, "data> ") == NULL)
        {
        if (len > 0)    /* Reached EOF without hitting a newline */
          {
          fprintf(outfile, "\n");
          break;
          }
        done = 1;
        goto CONTINUE;
        }
      if (infile != stdin) fprintf(outfile, "%s", (char *)buffer);
      len = (int)strlen((char *)buffer);
      if (buffer[len-1] == '\n') break;
      }

    while (len > 0 && isspace(buffer[len-1])) len--;
    buffer[len] = 0;
    if (len == 0) break;

    p = buffer;
    while (isspace(*p)) p++;

#ifndef NOUTF
    /* Check that the data is well-formed UTF-8 if we're in UTF mode. To create
    invalid input to pcre_exec, you must use \x?? or \x{} sequences. */

    if (use_utf)
      {
      pcre_uint8 *q;
      pcre_uint32 cc;
      int n = 1;

      for (q = p; n > 0 && *q; q += n) n = utf82ord(q, &cc);
      if (n <= 0)
        {
        fprintf(outfile, "**Failed: invalid UTF-8 string cannot be used as input in UTF mode\n");
        goto NEXT_DATA;
        }
      }
#endif

#ifdef SUPPORT_VALGRIND
    /* Mark the dbuffer as addressable but undefined again. */

    if (dbuffer != NULL)
      {
      VALGRIND_MAKE_MEM_UNDEFINED(dbuffer, dbuffer_size * CHAR_SIZE);
      }
#endif

    /* Allocate a buffer to hold the data line; len+1 is an upper bound on
    the number of pcre_uchar units that will be needed. */

    while (dbuffer == NULL || (size_t)len >= dbuffer_size)
      {
      dbuffer_size *= 2;
      dbuffer = (pcre_uint8 *)realloc(dbuffer, dbuffer_size * CHAR_SIZE);
      if (dbuffer == NULL)
        {
        fprintf(stderr, "pcretest: realloc(%d) failed\n", (int)dbuffer_size);
        exit(1);
        }
      }

#ifdef SUPPORT_PCRE8
    q8 = (pcre_uint8 *) dbuffer;
#endif
#ifdef SUPPORT_PCRE16
    q16 = (pcre_uint16 *) dbuffer;
#endif
#ifdef SUPPORT_PCRE32
    q32 = (pcre_uint32 *) dbuffer;
#endif

    while ((c = *p++) != 0)
      {
      int i = 0;
      int n = 0;

      /* In UTF mode, input can be UTF-8, so just copy all non-backslash bytes.
      In non-UTF mode, allow the value of the byte to fall through to later,
      where values greater than 127 are turned into UTF-8 when running in
      16-bit or 32-bit mode. */

      if (c != '\\')
        {
#ifndef NOUTF
        if (use_utf && HASUTF8EXTRALEN(c)) { GETUTF8INC(c, p); }
#endif
        }

      /* Handle backslash escapes */

      else switch ((c = *p++))
        {
        case 'a': c =  CHAR_BEL; break;
        case 'b': c = '\b'; break;
        case 'e': c =  CHAR_ESC; break;
        case 'f': c = '\f'; break;
        case 'n': c = '\n'; break;
        case 'r': c = '\r'; break;
        case 't': c = '\t'; break;
        case 'v': c = '\v'; break;

        case '0': case '1': case '2': case '3':
        case '4': case '5': case '6': case '7':
        c -= '0';
        while (i++ < 2 && isdigit(*p) && *p != '8' && *p != '9')
          c = c * 8 + *p++ - '0';
        break;

        case 'o':
        if (*p == '{')
          {
          pcre_uint8 *pt = p;
          c = 0;
          for (pt++; isdigit(*pt) && *pt != '8' && *pt != '9'; pt++)
            {
            if (++i == 12)
              fprintf(outfile, "** Too many octal digits in \\o{...} item; "
                               "using only the first twelve.\n");
            else c = c * 8 + *pt - '0';
            }
          if (*pt == '}') p = pt + 1;
            else fprintf(outfile, "** Missing } after \\o{ (assumed)\n");
          }
        break;

        case 'x':
        if (*p == '{')
          {
          pcre_uint8 *pt = p;
          c = 0;

          /* We used to have "while (isxdigit(*(++pt)))" here, but it fails
          when isxdigit() is a macro that refers to its argument more than
          once. This is banned by the C Standard, but apparently happens in at
          least one MacOS environment. */

          for (pt++; isxdigit(*pt); pt++)
            {
            if (++i == 9)
              fprintf(outfile, "** Too many hex digits in \\x{...} item; "
                               "using only the first eight.\n");
            else c = c * 16 + tolower(*pt) - ((isdigit(*pt))? '0' : 'a' - 10);
            }
          if (*pt == '}')
            {
            p = pt + 1;
            break;
            }
          /* Not correct form for \x{...}; fall through */
          }

        /* \x without {} always defines just one byte in 8-bit mode. This
        allows UTF-8 characters to be constructed byte by byte, and also allows
        invalid UTF-8 sequences to be made. Just copy the byte in UTF mode.
        Otherwise, pass it down to later code so that it can be turned into
        UTF-8 when running in 16/32-bit mode. */

        c = 0;
        while (i++ < 2 && isxdigit(*p))
          {
          c = c * 16 + tolower(*p) - ((isdigit(*p))? '0' : 'a' - 10);
          p++;
          }
#if !defined NOUTF && defined SUPPORT_PCRE8
        if (use_utf && (pcre_mode == PCRE8_MODE))
          {
          *q8++ = c;
          continue;
          }
#endif
        break;

        case 0:   /* \ followed by EOF allows for an empty line */
        p--;
        continue;

        case '>':
        if (*p == '-')
          {
          start_offset_sign = -1;
          p++;
          }
        while(isdigit(*p)) start_offset = start_offset * 10 + *p++ - '0';
        start_offset *= start_offset_sign;
        continue;

        case 'A':  /* Option setting */
        options |= PCRE_ANCHORED;
        continue;

        case 'B':
        options |= PCRE_NOTBOL;
        continue;

        case 'C':
        if (isdigit(*p))    /* Set copy string */
          {
          while(isdigit(*p)) n = n * 10 + *p++ - '0';
          copystrings |= 1 << n;
          }
        else if (isalnum(*p))
          {
          READ_CAPTURE_NAME(p, &cn8ptr, &cn16ptr, &cn32ptr, re);
          }
        else if (*p == '+')
          {
          callout_extra = 1;
          p++;
          }
        else if (*p == '-')
          {
          SET_PCRE_CALLOUT(NULL);
          p++;
          }
        else if (*p == '!')
          {
          callout_fail_id = 0;
          p++;
          while(isdigit(*p))
            callout_fail_id = callout_fail_id * 10 + *p++ - '0';
          callout_fail_count = 0;
          if (*p == '!')
            {
            p++;
            while(isdigit(*p))
              callout_fail_count = callout_fail_count * 10 + *p++ - '0';
            }
          }
        else if (*p == '*')
          {
          int sign = 1;
          callout_data = 0;
          if (*(++p) == '-') { sign = -1; p++; }
          while(isdigit(*p))
            callout_data = callout_data * 10 + *p++ - '0';
          callout_data *= sign;
          callout_data_set = 1;
          }
        continue;

#if !defined NODFA
        case 'D':
#if !defined NOPOSIX
        if (posix || do_posix)
          printf("** Can't use dfa matching in POSIX mode: \\D ignored\n");
        else
#endif
          use_dfa = 1;
        continue;
#endif

#if !defined NODFA
        case 'F':
        options |= PCRE_DFA_SHORTEST;
        continue;
#endif

        case 'G':
        if (isdigit(*p))
          {
          while(isdigit(*p)) n = n * 10 + *p++ - '0';
          getstrings |= 1 << n;
          }
        else if (isalnum(*p))
          {
          READ_CAPTURE_NAME(p, &gn8ptr, &gn16ptr, &gn32ptr, re);
          }
        continue;

        case 'J':
        while(isdigit(*p)) n = n * 10 + *p++ - '0';
        if (extra != NULL
            && (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0
            && extra->executable_jit != NULL)
          {
          if (jit_stack != NULL) { PCRE_JIT_STACK_FREE(jit_stack); }
          jit_stack = PCRE_JIT_STACK_ALLOC(1, n * 1024);
          PCRE_ASSIGN_JIT_STACK(extra, jit_callback, jit_stack);
          }
        continue;

        case 'L':
        getlist = 1;
        continue;

        case 'M':
        find_match_limit = 1;
        continue;

        case 'N':
        if ((options & PCRE_NOTEMPTY) != 0)
          options = (options & ~PCRE_NOTEMPTY) | PCRE_NOTEMPTY_ATSTART;
        else
          options |= PCRE_NOTEMPTY;
        continue;

        case 'O':
        while(isdigit(*p)) n = n * 10 + *p++ - '0';
        if (n > size_offsets_max)
          {
          size_offsets_max = n;
          free(offsets);
          use_offsets = offsets = (int *)malloc(size_offsets_max * sizeof(int));
          if (offsets == NULL)
            {
            printf("** Failed to get %d bytes of memory for offsets vector\n",
              (int)(size_offsets_max * sizeof(int)));
            yield = 1;
            goto EXIT;
            }
          }
        use_size_offsets = n;
        if (n == 0) use_offsets = NULL;   /* Ensures it can't write to it */
          else use_offsets = offsets + size_offsets_max - n;  /* To catch overruns */
        continue;

        case 'P':
        options |= ((options & PCRE_PARTIAL_SOFT) == 0)?
          PCRE_PARTIAL_SOFT : PCRE_PARTIAL_HARD;
        continue;

        case 'Q':
        while(isdigit(*p)) n = n * 10 + *p++ - '0';
        if (extra == NULL)
          {
          extra = (pcre_extra *)malloc(sizeof(pcre_extra));
          extra->flags = 0;
          }
        extra->flags |= PCRE_EXTRA_MATCH_LIMIT_RECURSION;
        extra->match_limit_recursion = n;
        continue;

        case 'q':
        while(isdigit(*p)) n = n * 10 + *p++ - '0';
        if (extra == NULL)
          {
          extra = (pcre_extra *)malloc(sizeof(pcre_extra));
          extra->flags = 0;
          }
        extra->flags |= PCRE_EXTRA_MATCH_LIMIT;
        extra->match_limit = n;
        continue;

#if !defined NODFA
        case 'R':
        options |= PCRE_DFA_RESTART;
        continue;
#endif

        case 'S':
        show_malloc = 1;
        continue;

        case 'Y':
        options |= PCRE_NO_START_OPTIMIZE;
        continue;

        case 'Z':
        options |= PCRE_NOTEOL;
        continue;

        case '?':
        options |= PCRE_NO_UTF8_CHECK;
        continue;

        case '<':
          {
          int x = check_mc_option(p, outfile, TRUE, "escape sequence");
          if (x == 0) goto NEXT_DATA;
          options |= x;
          while (*p++ != '>');
          }
        continue;
        }

      /* We now have a character value in c that may be greater than 255.
      In 8-bit mode we convert to UTF-8 if we are in UTF mode. Values greater
      than 127 in UTF mode must have come from \x{...} or octal constructs
      because values from \x.. get this far only in non-UTF mode. */

#ifdef SUPPORT_PCRE8
      if (pcre_mode == PCRE8_MODE)
        {
#ifndef NOUTF
        if (use_utf)
          {
          if (c > 0x7fffffff)
            {
            fprintf(outfile, "** Character \\x{%x} is greater than 0x7fffffff "
              "and so cannot be converted to UTF-8\n", c);
            goto NEXT_DATA;
            }
          q8 += ord2utf8(c, q8);
          }
        else
#endif
          {
          if (c > 0xffu)
            {
            fprintf(outfile, "** Character \\x{%x} is greater than 255 "
              "and UTF-8 mode is not enabled.\n", c);
            fprintf(outfile, "** Truncation will probably give the wrong "
              "result.\n");
            }
          *q8++ = c;
          }
        }
#endif
#ifdef SUPPORT_PCRE16
      if (pcre_mode == PCRE16_MODE)
        {
#ifndef NOUTF
        if (use_utf)
          {
          if (c > 0x10ffffu)
            {
            fprintf(outfile, "** Failed: character \\x{%x} is greater than "
              "0x10ffff and so cannot be converted to UTF-16\n", c);
            goto NEXT_DATA;
            }
          else if (c >= 0x10000u)
            {
            c-= 0x10000u;
            *q16++ = 0xD800 | (c >> 10);
            *q16++ = 0xDC00 | (c & 0x3ff);
            }
          else
            *q16++ = c;
          }
        else
#endif
          {
          if (c > 0xffffu)
            {
            fprintf(outfile, "** Character \\x{%x} is greater than 0xffff "
              "and UTF-16 mode is not enabled.\n", c);
            fprintf(outfile, "** Truncation will probably give the wrong "
              "result.\n");
            }

          *q16++ = c;
          }
        }
#endif
#ifdef SUPPORT_PCRE32
      if (pcre_mode == PCRE32_MODE)
        {
        *q32++ = c;
        }
#endif

      }

    /* Reached end of subject string */

#ifdef SUPPORT_PCRE8
    if (pcre_mode == PCRE8_MODE)
    {
      *q8 = 0;
      len = (int)(q8 - (pcre_uint8 *)dbuffer);
    }
#endif
#ifdef SUPPORT_PCRE16
    if (pcre_mode == PCRE16_MODE)
    {
      *q16 = 0;
      len = (int)(q16 - (pcre_uint16 *)dbuffer);
    }
#endif
#ifdef SUPPORT_PCRE32
    if (pcre_mode == PCRE32_MODE)
    {
      *q32 = 0;
      len = (int)(q32 - (pcre_uint32 *)dbuffer);
    }
#endif

    /* If we're compiling with explicit valgrind support, Mark the data from after
    its end to the end of the buffer as unaddressable, so that a read over the end
    of the buffer will be seen by valgrind, even if it doesn't cause a crash.
    If we're not building with valgrind support, at least move the data to the end
    of the buffer so that it might at least cause a crash.
    If we are using the POSIX interface, we must include the terminating zero. */

    bptr = dbuffer;

#if !defined NOPOSIX
    if (posix || do_posix)
      {
#ifdef SUPPORT_VALGRIND
      VALGRIND_MAKE_MEM_NOACCESS(dbuffer + len + 1, dbuffer_size - (len + 1));
#else
      memmove(bptr + dbuffer_size - len - 1, bptr, len + 1);
      bptr += dbuffer_size - len - 1;
#endif
      }
    else
#endif
      {
#ifdef SUPPORT_VALGRIND
      VALGRIND_MAKE_MEM_NOACCESS(dbuffer + len * CHAR_SIZE, (dbuffer_size - len) * CHAR_SIZE);
#else
      bptr = memmove(bptr + (dbuffer_size - len) * CHAR_SIZE, bptr, len * CHAR_SIZE);
#endif
      }

    if ((all_use_dfa || use_dfa) && find_match_limit)
      {
      printf("**Match limit not relevant for DFA matching: ignored\n");
      find_match_limit = 0;
      }

    /* Handle matching via the POSIX interface, which does not
    support timing or playing with the match limit or callout data. */

#if !defined NOPOSIX
    if (posix || do_posix)
      {
      int rc;
      int eflags = 0;
      regmatch_t *pmatch = NULL;
      if (use_size_offsets > 0)
        pmatch = (regmatch_t *)malloc(sizeof(regmatch_t) * use_size_offsets);
      if ((options & PCRE_NOTBOL) != 0) eflags |= REG_NOTBOL;
      if ((options & PCRE_NOTEOL) != 0) eflags |= REG_NOTEOL;
      if ((options & PCRE_NOTEMPTY) != 0) eflags |= REG_NOTEMPTY;

      rc = regexec(&preg, (const char *)bptr, use_size_offsets, pmatch, eflags);

      if (rc != 0)
        {
        (void)regerror(rc, &preg, (char *)buffer, buffer_size);
        fprintf(outfile, "No match: POSIX code %d: %s\n", rc, buffer);
        }
      else if ((REAL_PCRE_OPTIONS(preg.re_pcre) & PCRE_NO_AUTO_CAPTURE) != 0)
        {
        fprintf(outfile, "Matched with REG_NOSUB\n");
        }
      else
        {
        size_t i;
        for (i = 0; i < (size_t)use_size_offsets; i++)
          {
          if (pmatch[i].rm_so >= 0)
            {
            fprintf(outfile, "%2d: ", (int)i);
            PCHARSV(dbuffer, pmatch[i].rm_so,
              pmatch[i].rm_eo - pmatch[i].rm_so, outfile);
            fprintf(outfile, "\n");
            if (do_showcaprest || (i == 0 && do_showrest))
              {
              fprintf(outfile, "%2d+ ", (int)i);
              PCHARSV(dbuffer, pmatch[i].rm_eo, len - pmatch[i].rm_eo,
                outfile);
              fprintf(outfile, "\n");
              }
            }
          }
        }
      free(pmatch);
      goto NEXT_DATA;
      }

#endif  /* !defined NOPOSIX */

    /* Handle matching via the native interface - repeats for /g and /G */

    /* Ensure that there is a JIT callback if we want to verify that JIT was
    actually used. If jit_stack == NULL, no stack has yet been assigned. */

    if (verify_jit && jit_stack == NULL && extra != NULL)
       { PCRE_ASSIGN_JIT_STACK(extra, jit_callback, jit_stack); }

    for (;; gmatched++)    /* Loop for /g or /G */
      {
      markptr = NULL;
      jit_was_used = FALSE;

      if (timeitm > 0)
        {
        register int i;
        clock_t time_taken;
        clock_t start_time = clock();

#if !defined NODFA
        if (all_use_dfa || use_dfa)
          {
          if ((options & PCRE_DFA_RESTART) != 0)
            {
            fprintf(outfile, "Timing DFA restarts is not supported\n");
            break;
            }
          if (dfa_workspace == NULL)
            dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int));
          for (i = 0; i < timeitm; i++)
            {
            PCRE_DFA_EXEC(count, re, extra, bptr, len, start_offset,
              (options | g_notempty), use_offsets, use_size_offsets,
              dfa_workspace, DFA_WS_DIMENSION);
            }
          }
        else
#endif

        for (i = 0; i < timeitm; i++)
          {
          PCRE_EXEC(count, re, extra, bptr, len, start_offset,
            (options | g_notempty), use_offsets, use_size_offsets);
          }
        total_match_time += (time_taken = clock() - start_time);
        fprintf(outfile, "Execute time %.4f milliseconds\n",
          (((double)time_taken * 1000.0) / (double)timeitm) /
            (double)CLOCKS_PER_SEC);
        }

      /* If find_match_limit is set, we want to do repeated matches with
      varying limits in order to find the minimum value for the match limit and
      for the recursion limit. The match limits are relevant only to the normal
      running of pcre_exec(), so disable the JIT optimization. This makes it
      possible to run the same set of tests with and without JIT externally
      requested. */

      if (find_match_limit)
        {
        if (extra != NULL) { PCRE_FREE_STUDY(extra); }
        extra = (pcre_extra *)malloc(sizeof(pcre_extra));
        extra->flags = 0;

        (void)check_match_limit(re, extra, bptr, len, start_offset,
          options|g_notempty, use_offsets, use_size_offsets,
          PCRE_EXTRA_MATCH_LIMIT, &(extra->match_limit),
          PCRE_ERROR_MATCHLIMIT, "match()");

        count = check_match_limit(re, extra, bptr, len, start_offset,
          options|g_notempty, use_offsets, use_size_offsets,
          PCRE_EXTRA_MATCH_LIMIT_RECURSION, &(extra->match_limit_recursion),
          PCRE_ERROR_RECURSIONLIMIT, "match() recursion");
        }

      /* If callout_data is set, use the interface with additional data */

      else if (callout_data_set)
        {
        if (extra == NULL)
          {
          extra = (pcre_extra *)malloc(sizeof(pcre_extra));
          extra->flags = 0;
          }
        extra->flags |= PCRE_EXTRA_CALLOUT_DATA;
        extra->callout_data = &callout_data;
        PCRE_EXEC(count, re, extra, bptr, len, start_offset,
          options | g_notempty, use_offsets, use_size_offsets);
        extra->flags &= ~PCRE_EXTRA_CALLOUT_DATA;
        }

      /* The normal case is just to do the match once, with the default
      value of match_limit. */

#if !defined NODFA
      else if (all_use_dfa || use_dfa)
        {
        if (dfa_workspace == NULL)
          dfa_workspace = (int *)malloc(DFA_WS_DIMENSION*sizeof(int));
        if (dfa_matched++ == 0)
          dfa_workspace[0] = -1;  /* To catch bad restart */
        PCRE_DFA_EXEC(count, re, extra, bptr, len, start_offset,
          (options | g_notempty), use_offsets, use_size_offsets, dfa_workspace,
          DFA_WS_DIMENSION);
        if (count == 0)
          {
          fprintf(outfile, "Matched, but offsets vector is too small to show all matches\n");
          count = use_size_offsets/2;
          }
        }
#endif

      else
        {
        PCRE_EXEC(count, re, extra, bptr, len, start_offset,
          options | g_notempty, use_offsets, use_size_offsets);
        if (count == 0)
          {
          fprintf(outfile, "Matched, but too many substrings\n");
          /* 2 is a special case; match can be returned */
          count = (use_size_offsets == 2)? 1 : use_size_offsets/3;
          }
        }

      /* Matched */

      if (count >= 0)
        {
        int i, maxcount;
        void *cnptr, *gnptr;

#if !defined NODFA
        if (all_use_dfa || use_dfa) maxcount = use_size_offsets/2; else
#endif
          /* 2 is a special case; match can be returned */
          maxcount = (use_size_offsets == 2)? 1 : use_size_offsets/3;

        /* This is a check against a lunatic return value. */

        if (count > maxcount)
          {
          fprintf(outfile,
            "** PCRE error: returned count %d is too big for offset size %d\n",
            count, use_size_offsets);
          count = use_size_offsets/3;
          if (do_g || do_G)
            {
            fprintf(outfile, "** /%c loop abandoned\n", do_g? 'g' : 'G');
            do_g = do_G = FALSE;        /* Break g/G loop */
            }
          }

        /* do_allcaps requests showing of all captures in the pattern, to check
        unset ones at the end. */

        if (do_allcaps)
          {
          if (new_info(re, NULL, PCRE_INFO_CAPTURECOUNT, &count) < 0)
            goto SKIP_DATA;
          count++;   /* Allow for full match */
          if (count * 2 > use_size_offsets) count = use_size_offsets/2;
          }

        /* Output the captured substrings. Note that, for the matched string,
        the use of \K in an assertion can make the start later than the end. */

        for (i = 0; i < count * 2; i += 2)
          {
          if (use_offsets[i] < 0)
            {
            if (use_offsets[i] != -1)
              fprintf(outfile, "ERROR: bad negative value %d for offset %d\n",
                use_offsets[i], i);
            if (use_offsets[i+1] != -1)
              fprintf(outfile, "ERROR: bad negative value %d for offset %d\n",
                use_offsets[i+1], i+1);
            fprintf(outfile, "%2d: <unset>\n", i/2);
            }
          else
            {
            int start = use_offsets[i];
            int end = use_offsets[i+1];

            if (start > end)
              {
              start = use_offsets[i+1];
              end = use_offsets[i];
              fprintf(outfile, "Start of matched string is beyond its end - "
                "displaying from end to start.\n");
              }

            fprintf(outfile, "%2d: ", i/2);
            PCHARSV(bptr, start, end - start, outfile);
            if (verify_jit && jit_was_used) fprintf(outfile, " (JIT)");
            fprintf(outfile, "\n");

            /* Note: don't use the start/end variables here because we want to
            show the text from what is reported as the end. */

            if (do_showcaprest || (i == 0 && do_showrest))
              {
              fprintf(outfile, "%2d+ ", i/2);
              PCHARSV(bptr, use_offsets[i+1], len - use_offsets[i+1],
                outfile);
              fprintf(outfile, "\n");
              }
            }
          }

        if (markptr != NULL)
          {
          fprintf(outfile, "MK: ");
          PCHARSV(markptr, 0, -1, outfile);
          fprintf(outfile, "\n");
          }

        for (i = 0; i < 32; i++)
          {
          if ((copystrings & (1 << i)) != 0)
            {
            int rc;
            char copybuffer[256];
            PCRE_COPY_SUBSTRING(rc, bptr, use_offsets, count, i,
              copybuffer, sizeof(copybuffer));
            if (rc < 0)
              fprintf(outfile, "copy substring %d failed %d\n", i, rc);
            else
              {
              fprintf(outfile, "%2dC ", i);
              PCHARSV(copybuffer, 0, rc, outfile);
              fprintf(outfile, " (%d)\n", rc);
              }
            }
          }

        cnptr = copynames;
        for (;;)
          {
          int rc;
          char copybuffer[256];

#ifdef SUPPORT_PCRE32
          if (pcre_mode == PCRE32_MODE)
            {
            if (*(pcre_uint32 *)cnptr == 0) break;
            }
#endif
#ifdef SUPPORT_PCRE16
          if (pcre_mode == PCRE16_MODE)
            {
            if (*(pcre_uint16 *)cnptr == 0) break;
            }
#endif
#ifdef SUPPORT_PCRE8
          if (pcre_mode == PCRE8_MODE)
            {
            if (*(pcre_uint8 *)cnptr == 0) break;
            }
#endif

          PCRE_COPY_NAMED_SUBSTRING(rc, re, bptr, use_offsets, count,
            cnptr, copybuffer, sizeof(copybuffer));

          if (rc < 0)
            {
            fprintf(outfile, "copy substring ");
            PCHARSV(cnptr, 0, -1, outfile);
            fprintf(outfile, " failed %d\n", rc);
            }
          else
            {
            fprintf(outfile, "  C ");
            PCHARSV(copybuffer, 0, rc, outfile);
            fprintf(outfile, " (%d) ", rc);
            PCHARSV(cnptr, 0, -1, outfile);
            putc('\n', outfile);
            }

          cnptr = (char *)cnptr + (STRLEN(cnptr) + 1) * CHAR_SIZE;
          }

        for (i = 0; i < 32; i++)
          {
          if ((getstrings & (1 << i)) != 0)
            {
            int rc;
            const char *substring;
            PCRE_GET_SUBSTRING(rc, bptr, use_offsets, count, i, &substring);
            if (rc < 0)
              fprintf(outfile, "get substring %d failed %d\n", i, rc);
            else
              {
              fprintf(outfile, "%2dG ", i);
              PCHARSV(substring, 0, rc, outfile);
              fprintf(outfile, " (%d)\n", rc);
              PCRE_FREE_SUBSTRING(substring);
              }
            }
          }

        gnptr = getnames;
        for (;;)
          {
          int rc;
          const char *substring;

#ifdef SUPPORT_PCRE32
          if (pcre_mode == PCRE32_MODE)
            {
            if (*(pcre_uint32 *)gnptr == 0) break;
            }
#endif
#ifdef SUPPORT_PCRE16
          if (pcre_mode == PCRE16_MODE)
            {
            if (*(pcre_uint16 *)gnptr == 0) break;
            }
#endif
#ifdef SUPPORT_PCRE8
          if (pcre_mode == PCRE8_MODE)
            {
            if (*(pcre_uint8 *)gnptr == 0) break;
            }
#endif

          PCRE_GET_NAMED_SUBSTRING(rc, re, bptr, use_offsets, count,
            gnptr, &substring);
          if (rc < 0)
            {
            fprintf(outfile, "get substring ");
            PCHARSV(gnptr, 0, -1, outfile);
            fprintf(outfile, " failed %d\n", rc);
            }
          else
            {
            fprintf(outfile, "  G ");
            PCHARSV(substring, 0, rc, outfile);
            fprintf(outfile, " (%d) ", rc);
            PCHARSV(gnptr, 0, -1, outfile);
            PCRE_FREE_SUBSTRING(substring);
            putc('\n', outfile);
            }

          gnptr = (char *)gnptr + (STRLEN(gnptr) + 1) * CHAR_SIZE;
          }

        if (getlist)
          {
          int rc;
          const char **stringlist;
          PCRE_GET_SUBSTRING_LIST(rc, bptr, use_offsets, count, &stringlist);
          if (rc < 0)
            fprintf(outfile, "get substring list failed %d\n", rc);
          else
            {
            for (i = 0; i < count; i++)
              {
              fprintf(outfile, "%2dL ", i);
              PCHARSV(stringlist[i], 0, -1, outfile);
              putc('\n', outfile);
              }
            if (stringlist[i] != NULL)
              fprintf(outfile, "string list not terminated by NULL\n");
            PCRE_FREE_SUBSTRING_LIST(stringlist);
            }
          }
        }

      /* There was a partial match. If the bumpalong point is not the same as
      the first inspected character, show the offset explicitly. */

      else if (count == PCRE_ERROR_PARTIAL)
        {
        fprintf(outfile, "Partial match");
        if (use_size_offsets > 2 && use_offsets[0] != use_offsets[2])
          fprintf(outfile, " at offset %d", use_offsets[2]);
        if (markptr != NULL)
          {
          fprintf(outfile, ", mark=");
          PCHARSV(markptr, 0, -1, outfile);
          }
        if (use_size_offsets > 1)
          {
          fprintf(outfile, ": ");
          PCHARSV(bptr, use_offsets[0], use_offsets[1] - use_offsets[0],
            outfile);
          }
        if (verify_jit && jit_was_used) fprintf(outfile, " (JIT)");
        fprintf(outfile, "\n");
        break;  /* Out of the /g loop */
        }

      /* Failed to match. If this is a /g or /G loop and we previously set
      g_notempty after a null match, this is not necessarily the end. We want
      to advance the start offset, and continue. We won't be at the end of the
      string - that was checked before setting g_notempty.

      Complication arises in the case when the newline convention is "any",
      "crlf", or "anycrlf". If the previous match was at the end of a line
      terminated by CRLF, an advance of one character just passes the \r,
      whereas we should prefer the longer newline sequence, as does the code in
      pcre_exec(). Fudge the offset value to achieve this. We check for a
      newline setting in the pattern; if none was set, use PCRE_CONFIG() to
      find the default.

      Otherwise, in the case of UTF-8 matching, the advance must be one
      character, not one byte. */

      else
        {
        if (g_notempty != 0)
          {
          int onechar = 1;
          unsigned int obits = REAL_PCRE_OPTIONS(re);
          use_offsets[0] = start_offset;
          if ((obits & PCRE_NEWLINE_BITS) == 0)
            {
            int d;
            (void)PCRE_CONFIG(PCRE_CONFIG_NEWLINE, &d);
            /* Note that these values are always the ASCII ones, even in
            EBCDIC environments. CR = 13, NL = 10. */
            obits = (d == 13)? PCRE_NEWLINE_CR :
                    (d == 10)? PCRE_NEWLINE_LF :
                    (d == (13<<8 | 10))? PCRE_NEWLINE_CRLF :
                    (d == -2)? PCRE_NEWLINE_ANYCRLF :
                    (d == -1)? PCRE_NEWLINE_ANY : 0;
            }
          if (((obits & PCRE_NEWLINE_BITS) == PCRE_NEWLINE_ANY ||
               (obits & PCRE_NEWLINE_BITS) == PCRE_NEWLINE_CRLF ||
               (obits & PCRE_NEWLINE_BITS) == PCRE_NEWLINE_ANYCRLF)
              &&
              start_offset < len - 1 && (
#ifdef SUPPORT_PCRE8
              (pcre_mode == PCRE8_MODE &&
               bptr[start_offset] == '\r' &&
               bptr[start_offset + 1] == '\n') ||
#endif
#ifdef SUPPORT_PCRE16
              (pcre_mode == PCRE16_MODE &&
               ((PCRE_SPTR16)bptr)[start_offset] == '\r' &&
               ((PCRE_SPTR16)bptr)[start_offset + 1] == '\n') ||
#endif
#ifdef SUPPORT_PCRE32
              (pcre_mode == PCRE32_MODE &&
               ((PCRE_SPTR32)bptr)[start_offset] == '\r' &&
               ((PCRE_SPTR32)bptr)[start_offset + 1] == '\n') ||
#endif
              0))
            onechar++;
          else if (use_utf)
            {
            while (start_offset + onechar < len)
              {
              if ((bptr[start_offset+onechar] & 0xc0) != 0x80) break;
              onechar++;
              }
            }
          use_offsets[1] = start_offset + onechar;
          }
        else
          {
          switch(count)
            {
            case PCRE_ERROR_NOMATCH:
            if (gmatched == 0)
              {
              if (markptr == NULL)
                {
                fprintf(outfile, "No match");
                }
              else
                {
                fprintf(outfile, "No match, mark = ");
                PCHARSV(markptr, 0, -1, outfile);
                }
              if (verify_jit && jit_was_used) fprintf(outfile, " (JIT)");
              putc('\n', outfile);
              }
            break;

            case PCRE_ERROR_BADUTF8:
            case PCRE_ERROR_SHORTUTF8:
            fprintf(outfile, "Error %d (%s UTF-%d string)", count,
              (count == PCRE_ERROR_BADUTF8)? "bad" : "short",
              8 * CHAR_SIZE);
            if (use_size_offsets >= 2)
              fprintf(outfile, " offset=%d reason=%d", use_offsets[0],
                use_offsets[1]);
            fprintf(outfile, "\n");
            break;

            case PCRE_ERROR_BADUTF8_OFFSET:
            fprintf(outfile, "Error %d (bad UTF-%d offset)\n", count,
              8 * CHAR_SIZE);
            break;

            default:
            if (count < 0 &&
                (-count) < (int)(sizeof(errtexts)/sizeof(const char *)))
              fprintf(outfile, "Error %d (%s)\n", count, errtexts[-count]);
            else
              fprintf(outfile, "Error %d (Unexpected value)\n", count);
            break;
            }

          break;  /* Out of the /g loop */
          }
        }

      /* If not /g or /G we are done */

      if (!do_g && !do_G) break;

      if (use_offsets == NULL)
        {
        fprintf(outfile, "Cannot do global matching without an ovector\n");
        break;
        }

      if (use_size_offsets < 2)
        {
        fprintf(outfile, "Cannot do global matching with an ovector size < 2\n");
        break;
        }

      /* If we have matched an empty string, first check to see if we are at
      the end of the subject. If so, the /g loop is over. Otherwise, mimic what
      Perl's /g options does. This turns out to be rather cunning. First we set
      PCRE_NOTEMPTY_ATSTART and PCRE_ANCHORED and try the match again at the
      same point. If this fails (picked up above) we advance to the next
      character. */

      g_notempty = 0;

      if (use_offsets[0] == use_offsets[1])
        {
        if (use_offsets[0] == len) break;
        g_notempty = PCRE_NOTEMPTY_ATSTART | PCRE_ANCHORED;
        }

      /* For /g, update the start offset, leaving the rest alone. There is a
      tricky case when \K is used in a positive lookbehind assertion. This can
      cause the end of the match to be less than or equal to the start offset.
      In this case we restart at one past the start offset. This may return the
      same match if the original start offset was bumped along during the
      match, but eventually the new start offset will hit the actual start
      offset. (In PCRE2 the true start offset is available, and this can be
      done better. It is not worth doing more than making sure we do not loop
      at this stage in the life of PCRE1.) */

      if (do_g)
        {
        if (g_notempty == 0 && use_offsets[1] <= start_offset)
          {
          if (start_offset >= len) break;  /* End of subject */
          start_offset++;
          if (use_utf)
            {
            while (start_offset < len)
              {
              if ((bptr[start_offset] & 0xc0) != 0x80) break;
              start_offset++;
              }
            }
          }
        else start_offset = use_offsets[1];
        }

      /* For /G, update the pointer and length */

      else
        {
        bptr += use_offsets[1] * CHAR_SIZE;
        len -= use_offsets[1];
        }
      }  /* End of loop for /g and /G */

    NEXT_DATA: continue;
    }    /* End of loop for data lines */

  CONTINUE:

#if !defined NOPOSIX
  if ((posix || do_posix) && preg.re_pcre != 0) regfree(&preg);
#endif

  if (re != NULL) new_free(re);
  if (extra != NULL)
    {
    PCRE_FREE_STUDY(extra);
    }
  if (locale_set)
    {
    new_free((void *)tables);
    setlocale(LC_CTYPE, "C");
    locale_set = 0;
    }
  if (jit_stack != NULL)
    {
    PCRE_JIT_STACK_FREE(jit_stack);
    jit_stack = NULL;
    }
  }

if (infile == stdin) fprintf(outfile, "\n");

if (showtotaltimes)
  {
  fprintf(outfile, "--------------------------------------\n");
  if (timeit > 0)
    {
    fprintf(outfile, "Total compile time %.4f milliseconds\n",
      (((double)total_compile_time * 1000.0) / (double)timeit) /
        (double)CLOCKS_PER_SEC);
    fprintf(outfile, "Total study time   %.4f milliseconds\n",
      (((double)total_study_time * 1000.0) / (double)timeit) /
        (double)CLOCKS_PER_SEC);
    }
  fprintf(outfile, "Total execute time %.4f milliseconds\n",
    (((double)total_match_time * 1000.0) / (double)timeitm) /
      (double)CLOCKS_PER_SEC);
  }

EXIT:

if (infile != NULL && infile != stdin) fclose(infile);
if (outfile != NULL && outfile != stdout) fclose(outfile);

free(buffer);
free(dbuffer);
free(pbuffer);
free(offsets);

#ifdef SUPPORT_PCRE16
if (buffer16 != NULL) free(buffer16);
#endif
#ifdef SUPPORT_PCRE32
if (buffer32 != NULL) free(buffer32);
#endif

#if !defined NODFA
if (dfa_workspace != NULL)
  free(dfa_workspace);
#endif

#if defined(__VMS)
  yield = SS$_NORMAL;  /* Return values via DCL symbols */
#endif

return yield;
}