Diff for /win32/pcre/pcre_study.c between versions 1.4 and 1.7

version 1.4, 2011/02/01 05:03:42 version 1.7, 2015/10/13 00:25:41
Line 6 Line 6
 and semantics are as close as possible to those of the Perl 5 language.  and semantics are as close as possible to those of the Perl 5 language.
   
                        Written by Philip Hazel                         Written by Philip Hazel
            Copyright (c) 1997-2010 University of Cambridge             Copyright (c) 1997-2012 University of Cambridge
   
 -----------------------------------------------------------------------------  -----------------------------------------------------------------------------
 Redistribution and use in source and binary forms, with or without  Redistribution and use in source and binary forms, with or without
Line 52  supporting functions. */ Line 52  supporting functions. */
   
 /* Returns from set_start_bits() */  /* Returns from set_start_bits() */
   
 enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE };  enum { SSB_FAIL, SSB_DONE, SSB_CONTINUE, SSB_UNKNOWN };
   
   
   
Line 66  string of that length that matches. In U Line 66  string of that length that matches. In U
 rather than bytes.  rather than bytes.
   
 Arguments:  Arguments:
   code       pointer to start of group (the bracket)    re              compiled pattern block
   startcode  pointer to start of the whole pattern    code            pointer to start of group (the bracket)
   options    the compiling options    startcode       pointer to start of the whole pattern's code
     options         the compiling options
     recurses        chain of recurse_check to catch mutual recursion
   
 Returns:   the minimum length  Returns:   the minimum length
            -1 if \C was encountered             -1 if \C in UTF-8 mode or (*ACCEPT) was encountered
            -2 internal error (missing capturing bracket)             -2 internal error (missing capturing bracket)
              -3 internal error (opcode not listed)
 */  */
   
 static int  static int
 find_minlength(const uschar *code, const uschar *startcode, int options)  find_minlength(const REAL_PCRE *re, const pcre_uchar *code,
     const pcre_uchar *startcode, int options, recurse_check *recurses)
 {  {
 int length = -1;  int length = -1;
 BOOL utf8 = (options & PCRE_UTF8) != 0;  /* PCRE_UTF16 has the same value as PCRE_UTF8. */
   BOOL utf = (options & PCRE_UTF8) != 0;
 BOOL had_recurse = FALSE;  BOOL had_recurse = FALSE;
   recurse_check this_recurse;
 register int branchlength = 0;  register int branchlength = 0;
 register uschar *cc = (uschar *)code + 1 + LINK_SIZE;  register pcre_uchar *cc = (pcre_uchar *)code + 1 + LINK_SIZE;
   
 if (*code == OP_CBRA || *code == OP_SCBRA) cc += 2;  if (*code == OP_CBRA || *code == OP_SCBRA ||
       *code == OP_CBRAPOS || *code == OP_SCBRAPOS) cc += IMM2_SIZE;
   
 /* Scan along the opcodes for this branch. If we get to the end of the  /* Scan along the opcodes for this branch. If we get to the end of the
 branch, check the length against that of the other branches. */  branch, check the length against that of the other branches. */
Line 92  branch, check the length against that of Line 99  branch, check the length against that of
 for (;;)  for (;;)
   {    {
   int d, min;    int d, min;
   uschar *cs, *ce;    pcre_uchar *cs, *ce;
   register int op = *cc;    register pcre_uchar op = *cc;
   
   switch (op)    switch (op)
     {      {
Line 118  for (;;) Line 125  for (;;)
     case OP_SCBRA:      case OP_SCBRA:
     case OP_BRA:      case OP_BRA:
     case OP_SBRA:      case OP_SBRA:
       case OP_CBRAPOS:
       case OP_SCBRAPOS:
       case OP_BRAPOS:
       case OP_SBRAPOS:
     case OP_ONCE:      case OP_ONCE:
     d = find_minlength(cc, startcode, options);      case OP_ONCE_NC:
       d = find_minlength(re, cc, startcode, options, recurses);
     if (d < 0) return d;      if (d < 0) return d;
     branchlength += d;      branchlength += d;
     do cc += GET(cc, 1); while (*cc == OP_ALT);      do cc += GET(cc, 1); while (*cc == OP_ALT);
     cc += 1 + LINK_SIZE;      cc += 1 + LINK_SIZE;
     break;      break;
   
       /* ACCEPT makes things far too complicated; we have to give up. */
   
       case OP_ACCEPT:
       case OP_ASSERT_ACCEPT:
       return -1;
   
     /* Reached end of a branch; if it's a ket it is the end of a nested      /* Reached end of a branch; if it's a ket it is the end of a nested
     call. If it's ALT it is an alternation in a nested call. If it is      call. If it's ALT it is an alternation in a nested call. If it is END it's
     END it's the end of the outer call. All can be handled by the same code. */      the end of the outer call. All can be handled by the same code. If an
       ACCEPT was previously encountered, use the length that was in force at that
       time, and pass back the shortest ACCEPT length. */
   
     case OP_ALT:      case OP_ALT:
     case OP_KET:      case OP_KET:
     case OP_KETRMAX:      case OP_KETRMAX:
     case OP_KETRMIN:      case OP_KETRMIN:
       case OP_KETRPOS:
     case OP_END:      case OP_END:
     if (length < 0 || (!had_recurse && branchlength < length))      if (length < 0 || (!had_recurse && branchlength < length))
       length = branchlength;        length = branchlength;
     if (*cc != OP_ALT) return length;      if (op != OP_ALT) return length;
     cc += 1 + LINK_SIZE;      cc += 1 + LINK_SIZE;
     branchlength = 0;      branchlength = 0;
     had_recurse = FALSE;      had_recurse = FALSE;
Line 156  for (;;) Line 177  for (;;)
   
     case OP_REVERSE:      case OP_REVERSE:
     case OP_CREF:      case OP_CREF:
     case OP_NCREF:      case OP_DNCREF:
     case OP_RREF:      case OP_RREF:
     case OP_NRREF:      case OP_DNRREF:
     case OP_DEF:      case OP_DEF:
     case OP_OPT:  
     case OP_CALLOUT:      case OP_CALLOUT:
     case OP_SOD:      case OP_SOD:
     case OP_SOM:      case OP_SOM:
     case OP_EOD:      case OP_EOD:
     case OP_EODN:      case OP_EODN:
     case OP_CIRC:      case OP_CIRC:
       case OP_CIRCM:
     case OP_DOLL:      case OP_DOLL:
       case OP_DOLLM:
     case OP_NOT_WORD_BOUNDARY:      case OP_NOT_WORD_BOUNDARY:
     case OP_WORD_BOUNDARY:      case OP_WORD_BOUNDARY:
     cc += _pcre_OP_lengths[*cc];      cc += PRIV(OP_lengths)[*cc];
     break;      break;
   
     /* Skip over a subpattern that has a {0} or {0,x} quantifier */      /* Skip over a subpattern that has a {0} or {0,x} quantifier */
   
     case OP_BRAZERO:      case OP_BRAZERO:
     case OP_BRAMINZERO:      case OP_BRAMINZERO:
       case OP_BRAPOSZERO:
     case OP_SKIPZERO:      case OP_SKIPZERO:
     cc += _pcre_OP_lengths[*cc];      cc += PRIV(OP_lengths)[*cc];
     do cc += GET(cc, 1); while (*cc == OP_ALT);      do cc += GET(cc, 1); while (*cc == OP_ALT);
     cc += 1 + LINK_SIZE;      cc += 1 + LINK_SIZE;
     break;      break;
Line 186  for (;;) Line 209  for (;;)
     /* Handle literal characters and + repetitions */      /* Handle literal characters and + repetitions */
   
     case OP_CHAR:      case OP_CHAR:
     case OP_CHARNC:      case OP_CHARI:
     case OP_NOT:      case OP_NOT:
       case OP_NOTI:
     case OP_PLUS:      case OP_PLUS:
       case OP_PLUSI:
     case OP_MINPLUS:      case OP_MINPLUS:
       case OP_MINPLUSI:
     case OP_POSPLUS:      case OP_POSPLUS:
       case OP_POSPLUSI:
     case OP_NOTPLUS:      case OP_NOTPLUS:
       case OP_NOTPLUSI:
     case OP_NOTMINPLUS:      case OP_NOTMINPLUS:
       case OP_NOTMINPLUSI:
     case OP_NOTPOSPLUS:      case OP_NOTPOSPLUS:
       case OP_NOTPOSPLUSI:
     branchlength++;      branchlength++;
     cc += 2;      cc += 2;
 #ifdef SUPPORT_UTF8  #ifdef SUPPORT_UTF
     if (utf8 && cc[-1] >= 0xc0) cc += _pcre_utf8_table4[cc[-1] & 0x3f];      if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
 #endif  #endif
     break;      break;
   
Line 212  for (;;) Line 242  for (;;)
     need to skip over a multibyte character in UTF8 mode.  */      need to skip over a multibyte character in UTF8 mode.  */
   
     case OP_EXACT:      case OP_EXACT:
       case OP_EXACTI:
     case OP_NOTEXACT:      case OP_NOTEXACT:
       case OP_NOTEXACTI:
     branchlength += GET2(cc,1);      branchlength += GET2(cc,1);
     cc += 4;      cc += 2 + IMM2_SIZE;
 #ifdef SUPPORT_UTF8  #ifdef SUPPORT_UTF
     if (utf8 && cc[-1] >= 0xc0) cc += _pcre_utf8_table4[cc[-1] & 0x3f];      if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
 #endif  #endif
     break;      break;
   
     case OP_TYPEEXACT:      case OP_TYPEEXACT:
     branchlength += GET2(cc,1);      branchlength += GET2(cc,1);
     cc += (cc[3] == OP_PROP || cc[3] == OP_NOTPROP)? 6 : 4;      cc += 2 + IMM2_SIZE + ((cc[1 + IMM2_SIZE] == OP_PROP
         || cc[1 + IMM2_SIZE] == OP_NOTPROP)? 2 : 0);
     break;      break;
   
     /* Handle single-char non-literal matchers */      /* Handle single-char non-literal matchers */
Line 249  for (;;) Line 282  for (;;)
     cc++;      cc++;
     break;      break;
   
     /* "Any newline" might match two characters */      /* "Any newline" might match two characters, but it also might match just
       one. */
   
     case OP_ANYNL:      case OP_ANYNL:
     branchlength += 2;      branchlength += 1;
     cc++;      cc++;
     break;      break;
   
     /* The single-byte matcher means we can't proceed in UTF-8 mode */      /* The single-byte matcher means we can't proceed in UTF-8 mode. (In
       non-UTF-8 mode \C will actually be turned into OP_ALLANY, so won't ever
       appear, but leave the code, just in case.) */
   
     case OP_ANYBYTE:      case OP_ANYBYTE:
 #ifdef SUPPORT_UTF8  #ifdef SUPPORT_UTF
     if (utf8) return -1;      if (utf) return -1;
 #endif  #endif
     branchlength++;      branchlength++;
     cc++;      cc++;
Line 276  for (;;) Line 312  for (;;)
     case OP_TYPEPOSSTAR:      case OP_TYPEPOSSTAR:
     case OP_TYPEPOSQUERY:      case OP_TYPEPOSQUERY:
     if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;      if (cc[1] == OP_PROP || cc[1] == OP_NOTPROP) cc += 2;
     cc += _pcre_OP_lengths[op];      cc += PRIV(OP_lengths)[op];
     break;      break;
   
     case OP_TYPEUPTO:      case OP_TYPEUPTO:
     case OP_TYPEMINUPTO:      case OP_TYPEMINUPTO:
     case OP_TYPEPOSUPTO:      case OP_TYPEPOSUPTO:
     if (cc[3] == OP_PROP || cc[3] == OP_NOTPROP) cc += 2;      if (cc[1 + IMM2_SIZE] == OP_PROP
     cc += _pcre_OP_lengths[op];        || cc[1 + IMM2_SIZE] == OP_NOTPROP) cc += 2;
       cc += PRIV(OP_lengths)[op];
     break;      break;
   
     /* Check a class for variable quantification */      /* Check a class for variable quantification */
   
 #ifdef SUPPORT_UTF8  
     case OP_XCLASS:  
     cc += GET(cc, 1) - 33;  
     /* Fall through */  
 #endif  
   
     case OP_CLASS:      case OP_CLASS:
     case OP_NCLASS:      case OP_NCLASS:
     cc += 33;  #if defined SUPPORT_UTF || defined COMPILE_PCRE16 || defined COMPILE_PCRE32
       case OP_XCLASS:
       /* The original code caused an unsigned overflow in 64 bit systems,
       so now we use a conditional statement. */
       if (op == OP_XCLASS)
         cc += GET(cc, 1);
       else
         cc += PRIV(OP_lengths)[OP_CLASS];
   #else
       cc += PRIV(OP_lengths)[OP_CLASS];
   #endif
   
     switch (*cc)      switch (*cc)
       {        {
       case OP_CRPLUS:        case OP_CRPLUS:
       case OP_CRMINPLUS:        case OP_CRMINPLUS:
         case OP_CRPOSPLUS:
       branchlength++;        branchlength++;
       /* Fall through */        /* Fall through */
   
Line 309  for (;;) Line 351  for (;;)
       case OP_CRMINSTAR:        case OP_CRMINSTAR:
       case OP_CRQUERY:        case OP_CRQUERY:
       case OP_CRMINQUERY:        case OP_CRMINQUERY:
         case OP_CRPOSSTAR:
         case OP_CRPOSQUERY:
       cc++;        cc++;
       break;        break;
   
       case OP_CRRANGE:        case OP_CRRANGE:
       case OP_CRMINRANGE:        case OP_CRMINRANGE:
         case OP_CRPOSRANGE:
       branchlength += GET2(cc,1);        branchlength += GET2(cc,1);
       cc += 5;        cc += 1 + 2 * IMM2_SIZE;
       break;        break;
   
       default:        default:
Line 336  for (;;) Line 381  for (;;)
     matches an empty string (by default it causes a matching failure), so in      matches an empty string (by default it causes a matching failure), so in
     that case we must set the minimum length to zero. */      that case we must set the minimum length to zero. */
   
     case OP_REF:      case OP_DNREF:     /* Duplicate named pattern back reference */
       case OP_DNREFI:
     if ((options & PCRE_JAVASCRIPT_COMPAT) == 0)      if ((options & PCRE_JAVASCRIPT_COMPAT) == 0)
       {        {
       ce = cs = (uschar *)_pcre_find_bracket(startcode, utf8, GET2(cc, 1));        int count = GET2(cc, 1+IMM2_SIZE);
         pcre_uchar *slot = (pcre_uchar *)re +
           re->name_table_offset + GET2(cc, 1) * re->name_entry_size;
         d = INT_MAX;
         while (count-- > 0)
           {
           ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(slot, 0));
           if (cs == NULL) return -2;
           do ce += GET(ce, 1); while (*ce == OP_ALT);
           if (cc > cs && cc < ce)     /* Simple recursion */
             {
             d = 0;
             had_recurse = TRUE;
             break;
             }
           else
             {
             recurse_check *r = recurses;
             for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
             if (r != NULL)           /* Mutual recursion */
               {
               d = 0;
               had_recurse = TRUE;
               break;
               }
             else
               {
               int dd;
               this_recurse.prev = recurses;
               this_recurse.group = cs;
               dd = find_minlength(re, cs, startcode, options, &this_recurse);
               if (dd < d) d = dd;
               }
             }
           slot += re->name_entry_size;
           }
         }
       else d = 0;
       cc += 1 + 2*IMM2_SIZE;
       goto REPEAT_BACK_REFERENCE;
   
       case OP_REF:      /* Single back reference */
       case OP_REFI:
       if ((options & PCRE_JAVASCRIPT_COMPAT) == 0)
         {
         ce = cs = (pcre_uchar *)PRIV(find_bracket)(startcode, utf, GET2(cc, 1));
       if (cs == NULL) return -2;        if (cs == NULL) return -2;
       do ce += GET(ce, 1); while (*ce == OP_ALT);        do ce += GET(ce, 1); while (*ce == OP_ALT);
       if (cc > cs && cc < ce)        if (cc > cs && cc < ce)    /* Simple recursion */
         {          {
         d = 0;          d = 0;
         had_recurse = TRUE;          had_recurse = TRUE;
         }          }
       else d = find_minlength(cs, startcode, options);        else
           {
           recurse_check *r = recurses;
           for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
           if (r != NULL)           /* Mutual recursion */
             {
             d = 0;
             had_recurse = TRUE;
             }
           else
             {
             this_recurse.prev = recurses;
             this_recurse.group = cs;
             d = find_minlength(re, cs, startcode, options, &this_recurse);
             }
           }
       }        }
     else d = 0;      else d = 0;
     cc += 3;      cc += 1 + IMM2_SIZE;
   
     /* Handle repeated back references */      /* Handle repeated back references */
   
       REPEAT_BACK_REFERENCE:
     switch (*cc)      switch (*cc)
       {        {
       case OP_CRSTAR:        case OP_CRSTAR:
       case OP_CRMINSTAR:        case OP_CRMINSTAR:
       case OP_CRQUERY:        case OP_CRQUERY:
       case OP_CRMINQUERY:        case OP_CRMINQUERY:
         case OP_CRPOSSTAR:
         case OP_CRPOSQUERY:
       min = 0;        min = 0;
       cc++;        cc++;
       break;        break;
   
         case OP_CRPLUS:
         case OP_CRMINPLUS:
         case OP_CRPOSPLUS:
         min = 1;
         cc++;
         break;
   
       case OP_CRRANGE:        case OP_CRRANGE:
       case OP_CRMINRANGE:        case OP_CRMINRANGE:
         case OP_CRPOSRANGE:
       min = GET2(cc, 1);        min = GET2(cc, 1);
       cc += 5;        cc += 1 + 2 * IMM2_SIZE;
       break;        break;
   
       default:        default:
Line 378  for (;;) Line 495  for (;;)
     branchlength += min * d;      branchlength += min * d;
     break;      break;
   
       /* We can easily detect direct recursion, but not mutual recursion. This is
       caught by a recursion depth count. */
   
     case OP_RECURSE:      case OP_RECURSE:
     cs = ce = (uschar *)startcode + GET(cc, 1);      cs = ce = (pcre_uchar *)startcode + GET(cc, 1);
     if (cs == NULL) return -2;  
     do ce += GET(ce, 1); while (*ce == OP_ALT);      do ce += GET(ce, 1); while (*ce == OP_ALT);
     if (cc > cs && cc < ce)      if (cc > cs && cc < ce)    /* Simple recursion */
       had_recurse = TRUE;        had_recurse = TRUE;
     else      else
       branchlength += find_minlength(cs, startcode, options);        {
         recurse_check *r = recurses;
         for (r = recurses; r != NULL; r = r->prev) if (r->group == cs) break;
         if (r != NULL)           /* Mutual recursion */
           had_recurse = TRUE;
         else
           {
           this_recurse.prev = recurses;
           this_recurse.group = cs;
           branchlength += find_minlength(re, cs, startcode, options,
             &this_recurse);
           }
         }
     cc += 1 + LINK_SIZE;      cc += 1 + LINK_SIZE;
     break;      break;
   
     /* Anything else does not or need not match a character. We can get the      /* Anything else does not or need not match a character. We can get the
     item's length from the table, but for those that can match zero occurrences      item's length from the table, but for those that can match zero occurrences
     of a character, we must take special action for UTF-8 characters. */      of a character, we must take special action for UTF-8 characters. As it
       happens, the "NOT" versions of these opcodes are used at present only for
       ASCII characters, so they could be omitted from this list. However, in
       future that may change, so we include them here so as not to leave a
       gotcha for a future maintainer. */
   
     case OP_UPTO:      case OP_UPTO:
       case OP_UPTOI:
     case OP_NOTUPTO:      case OP_NOTUPTO:
       case OP_NOTUPTOI:
     case OP_MINUPTO:      case OP_MINUPTO:
       case OP_MINUPTOI:
     case OP_NOTMINUPTO:      case OP_NOTMINUPTO:
       case OP_NOTMINUPTOI:
     case OP_POSUPTO:      case OP_POSUPTO:
       case OP_POSUPTOI:
       case OP_NOTPOSUPTO:
       case OP_NOTPOSUPTOI:
   
     case OP_STAR:      case OP_STAR:
       case OP_STARI:
       case OP_NOTSTAR:
       case OP_NOTSTARI:
     case OP_MINSTAR:      case OP_MINSTAR:
       case OP_MINSTARI:
     case OP_NOTMINSTAR:      case OP_NOTMINSTAR:
       case OP_NOTMINSTARI:
     case OP_POSSTAR:      case OP_POSSTAR:
       case OP_POSSTARI:
     case OP_NOTPOSSTAR:      case OP_NOTPOSSTAR:
       case OP_NOTPOSSTARI:
   
     case OP_QUERY:      case OP_QUERY:
       case OP_QUERYI:
       case OP_NOTQUERY:
       case OP_NOTQUERYI:
     case OP_MINQUERY:      case OP_MINQUERY:
       case OP_MINQUERYI:
     case OP_NOTMINQUERY:      case OP_NOTMINQUERY:
       case OP_NOTMINQUERYI:
     case OP_POSQUERY:      case OP_POSQUERY:
       case OP_POSQUERYI:
     case OP_NOTPOSQUERY:      case OP_NOTPOSQUERY:
     cc += _pcre_OP_lengths[op];      case OP_NOTPOSQUERYI:
 #ifdef SUPPORT_UTF8  
     if (utf8 && cc[-1] >= 0xc0) cc += _pcre_utf8_table4[cc[-1] & 0x3f];      cc += PRIV(OP_lengths)[op];
   #ifdef SUPPORT_UTF
       if (utf && HAS_EXTRALEN(cc[-1])) cc += GET_EXTRALEN(cc[-1]);
 #endif  #endif
     break;      break;
   
Line 419  for (;;) Line 578  for (;;)
     case OP_MARK:      case OP_MARK:
     case OP_PRUNE_ARG:      case OP_PRUNE_ARG:
     case OP_SKIP_ARG:      case OP_SKIP_ARG:
     cc += _pcre_OP_lengths[op] + cc[1];      case OP_THEN_ARG:
       cc += PRIV(OP_lengths)[op] + cc[1];
     break;      break;
   
     case OP_THEN_ARG:      /* The remaining opcodes are just skipped over. */
     cc += _pcre_OP_lengths[op] + cc[1+LINK_SIZE];  
       case OP_CLOSE:
       case OP_COMMIT:
       case OP_FAIL:
       case OP_PRUNE:
       case OP_SET_SOM:
       case OP_SKIP:
       case OP_THEN:
       cc += PRIV(OP_lengths)[op];
     break;      break;
   
     /* For the record, these are the opcodes that are matched by "default":      /* This should not occur: we list all opcodes explicitly so that when
     OP_ACCEPT, OP_CLOSE, OP_COMMIT, OP_FAIL, OP_PRUNE, OP_SET_SOM, OP_SKIP,      new ones get added they are properly considered. */
     OP_THEN. */  
   
     default:      default:
     cc += _pcre_OP_lengths[op];      return -3;
     break;  
     }      }
   }    }
 /* Control never gets here */  /* Control never gets here */
Line 454  Arguments: Line 620  Arguments:
   p             points to the character    p             points to the character
   caseless      the caseless flag    caseless      the caseless flag
   cd            the block with char table pointers    cd            the block with char table pointers
   utf8          TRUE for UTF-8 mode    utf           TRUE for UTF-8 / UTF-16 / UTF-32 mode
   
 Returns:        pointer after the character  Returns:        pointer after the character
 */  */
   
 static const uschar *  static const pcre_uchar *
 set_table_bit(uschar *start_bits, const uschar *p, BOOL caseless,  set_table_bit(pcre_uint8 *start_bits, const pcre_uchar *p, BOOL caseless,
   compile_data *cd, BOOL utf8)    compile_data *cd, BOOL utf)
 {  {
 unsigned int c = *p;  pcre_uint32 c = *p;
   
   #ifdef COMPILE_PCRE8
 SET_BIT(c);  SET_BIT(c);
   
 #ifdef SUPPORT_UTF8  #ifdef SUPPORT_UTF
 if (utf8 && c > 127)  if (utf && c > 127)
   {    {
   GETCHARINC(c, p);    GETCHARINC(c, p);
 #ifdef SUPPORT_UCP  #ifdef SUPPORT_UCP
   if (caseless)    if (caseless)
     {      {
     uschar buff[8];      pcre_uchar buff[6];
     c = UCD_OTHERCASE(c);      c = UCD_OTHERCASE(c);
     (void)_pcre_ord2utf8(c, buff);      (void)PRIV(ord2utf)(c, buff);
     SET_BIT(buff[0]);      SET_BIT(buff[0]);
     }      }
 #endif  #endif  /* Not SUPPORT_UCP */
   return p;    return p;
   }    }
 #endif  #else   /* Not SUPPORT_UTF */
   (void)(utf);   /* Stops warning for unused parameter */
   #endif  /* SUPPORT_UTF */
   
 /* Not UTF-8 mode, or character is less than 127. */  /* Not UTF-8 mode, or character is less than 127. */
   
 if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]);  if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]);
 return p + 1;  return p + 1;
   #endif  /* COMPILE_PCRE8 */
   
   #if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
   if (c > 0xff)
     {
     c = 0xff;
     caseless = FALSE;
     }
   SET_BIT(c);
   
   #ifdef SUPPORT_UTF
   if (utf && c > 127)
     {
     GETCHARINC(c, p);
   #ifdef SUPPORT_UCP
     if (caseless)
       {
       c = UCD_OTHERCASE(c);
       if (c > 0xff)
         c = 0xff;
       SET_BIT(c);
       }
   #endif  /* SUPPORT_UCP */
     return p;
     }
   #else   /* Not SUPPORT_UTF */
   (void)(utf);   /* Stops warning for unused parameter */
   #endif  /* SUPPORT_UTF */
   
   if (caseless && (cd->ctypes[c] & ctype_letter) != 0) SET_BIT(cd->fcc[c]);
   return p + 1;
   #endif
 }  }
   
   
Line 513  Returns:         nothing Line 714  Returns:         nothing
 */  */
   
 static void  static void
 set_type_bits(uschar *start_bits, int cbit_type, int table_limit,  set_type_bits(pcre_uint8 *start_bits, int cbit_type, unsigned int table_limit,
   compile_data *cd)    compile_data *cd)
 {  {
 register int c;  register pcre_uint32 c;
 for (c = 0; c < table_limit; c++) start_bits[c] |= cd->cbits[c+cbit_type];  for (c = 0; c < table_limit; c++) start_bits[c] |= cd->cbits[c+cbit_type];
   #if defined SUPPORT_UTF && defined COMPILE_PCRE8
 if (table_limit == 32) return;  if (table_limit == 32) return;
 for (c = 128; c < 256; c++)  for (c = 128; c < 256; c++)
   {    {
   if ((cd->cbits[c/8] & (1 << (c&7))) != 0)    if ((cd->cbits[c/8] & (1 << (c&7))) != 0)
     {      {
     uschar buff[8];      pcre_uchar buff[6];
     (void)_pcre_ord2utf8(c, buff);      (void)PRIV(ord2utf)(c, buff);
     SET_BIT(buff[0]);      SET_BIT(buff[0]);
     }      }
   }    }
   #endif
 }  }
   
   
Line 553  Returns:         nothing Line 756  Returns:         nothing
 */  */
   
 static void  static void
 set_nottype_bits(uschar *start_bits, int cbit_type, int table_limit,  set_nottype_bits(pcre_uint8 *start_bits, int cbit_type, unsigned int table_limit,
   compile_data *cd)    compile_data *cd)
 {  {
 register int c;  register pcre_uint32 c;
 for (c = 0; c < table_limit; c++) start_bits[c] |= ~cd->cbits[c+cbit_type];  for (c = 0; c < table_limit; c++) start_bits[c] |= ~cd->cbits[c+cbit_type];
   #if defined SUPPORT_UTF && defined COMPILE_PCRE8
 if (table_limit != 32) for (c = 24; c < 32; c++) start_bits[c] = 0xff;  if (table_limit != 32) for (c = 24; c < 32; c++) start_bits[c] = 0xff;
   #endif
 }  }
   
   
Line 578  function fails unless the result is SSB_ Line 783  function fails unless the result is SSB_
 Arguments:  Arguments:
   code         points to an expression    code         points to an expression
   start_bits   points to a 32-byte table, initialized to 0    start_bits   points to a 32-byte table, initialized to 0
   caseless     the current state of the caseless flag    utf          TRUE if in UTF-8 / UTF-16 / UTF-32 mode
   utf8         TRUE if in UTF-8 mode  
   cd           the block with char table pointers    cd           the block with char table pointers
   
 Returns:       SSB_FAIL     => Failed to find any starting bytes  Returns:       SSB_FAIL     => Failed to find any starting bytes
                SSB_DONE     => Found mandatory starting bytes                 SSB_DONE     => Found mandatory starting bytes
                SSB_CONTINUE => Found optional starting bytes                 SSB_CONTINUE => Found optional starting bytes
                  SSB_UNKNOWN  => Hit an unrecognized opcode
 */  */
   
 static int  static int
 set_start_bits(const uschar *code, uschar *start_bits, BOOL caseless,  set_start_bits(const pcre_uchar *code, pcre_uint8 *start_bits, BOOL utf,
   BOOL utf8, compile_data *cd)    compile_data *cd)
 {  {
 register int c;  register pcre_uint32 c;
 int yield = SSB_DONE;  int yield = SSB_DONE;
 int table_limit = utf8? 16:32;  #if defined SUPPORT_UTF && defined COMPILE_PCRE8
   int table_limit = utf? 16:32;
   #else
   int table_limit = 32;
   #endif
   
 #if 0  #if 0
 /* ========================================================================= */  /* ========================================================================= */
Line 614  volatile int dummy; Line 823  volatile int dummy;
   
 do  do
   {    {
   const uschar *tcode = code + (((int)*code == OP_CBRA)? 3:1) + LINK_SIZE;  
   BOOL try_next = TRUE;    BOOL try_next = TRUE;
     const pcre_uchar *tcode = code + 1 + LINK_SIZE;
   
     if (*code == OP_CBRA || *code == OP_SCBRA ||
         *code == OP_CBRAPOS || *code == OP_SCBRAPOS) tcode += IMM2_SIZE;
   
   while (try_next)    /* Loop for items in this branch */    while (try_next)    /* Loop for items in this branch */
     {      {
     int rc;      int rc;
   
     switch(*tcode)      switch(*tcode)
       {        {
       /* Fail if we reach something we don't understand */        /* If we reach something we don't understand, it means a new opcode has
         been created that hasn't been added to this code. Hopefully this problem
         will be discovered during testing. */
   
       default:        default:
         return SSB_UNKNOWN;
   
         /* Fail for a valid opcode that implies no starting bits. */
   
         case OP_ACCEPT:
         case OP_ASSERT_ACCEPT:
         case OP_ALLANY:
         case OP_ANY:
         case OP_ANYBYTE:
         case OP_CIRC:
         case OP_CIRCM:
         case OP_CLOSE:
         case OP_COMMIT:
         case OP_COND:
         case OP_CREF:
         case OP_DEF:
         case OP_DNCREF:
         case OP_DNREF:
         case OP_DNREFI:
         case OP_DNRREF:
         case OP_DOLL:
         case OP_DOLLM:
         case OP_END:
         case OP_EOD:
         case OP_EODN:
         case OP_EXTUNI:
         case OP_FAIL:
         case OP_MARK:
         case OP_NOT:
         case OP_NOTEXACT:
         case OP_NOTEXACTI:
         case OP_NOTI:
         case OP_NOTMINPLUS:
         case OP_NOTMINPLUSI:
         case OP_NOTMINQUERY:
         case OP_NOTMINQUERYI:
         case OP_NOTMINSTAR:
         case OP_NOTMINSTARI:
         case OP_NOTMINUPTO:
         case OP_NOTMINUPTOI:
         case OP_NOTPLUS:
         case OP_NOTPLUSI:
         case OP_NOTPOSPLUS:
         case OP_NOTPOSPLUSI:
         case OP_NOTPOSQUERY:
         case OP_NOTPOSQUERYI:
         case OP_NOTPOSSTAR:
         case OP_NOTPOSSTARI:
         case OP_NOTPOSUPTO:
         case OP_NOTPOSUPTOI:
         case OP_NOTPROP:
         case OP_NOTQUERY:
         case OP_NOTQUERYI:
         case OP_NOTSTAR:
         case OP_NOTSTARI:
         case OP_NOTUPTO:
         case OP_NOTUPTOI:
         case OP_NOT_HSPACE:
         case OP_NOT_VSPACE:
         case OP_PRUNE:
         case OP_PRUNE_ARG:
         case OP_RECURSE:
         case OP_REF:
         case OP_REFI:
         case OP_REVERSE:
         case OP_RREF:
         case OP_SCOND:
         case OP_SET_SOM:
         case OP_SKIP:
         case OP_SKIP_ARG:
         case OP_SOD:
         case OP_SOM:
         case OP_THEN:
         case OP_THEN_ARG:
       return SSB_FAIL;        return SSB_FAIL;
   
         /* A "real" property test implies no starting bits, but the fake property
         PT_CLIST identifies a list of characters. These lists are short, as they
         are used for characters with more than one "other case", so there is no
         point in recognizing them for OP_NOTPROP. */
   
         case OP_PROP:
         if (tcode[1] != PT_CLIST) return SSB_FAIL;
           {
           const pcre_uint32 *p = PRIV(ucd_caseless_sets) + tcode[2];
           while ((c = *p++) < NOTACHAR)
             {
   #if defined SUPPORT_UTF && defined COMPILE_PCRE8
             if (utf)
               {
               pcre_uchar buff[6];
               (void)PRIV(ord2utf)(c, buff);
               c = buff[0];
               }
   #endif
             if (c > 0xff) SET_BIT(0xff); else SET_BIT(c);
             }
           }
         try_next = FALSE;
         break;
   
         /* We can ignore word boundary tests. */
   
         case OP_WORD_BOUNDARY:
         case OP_NOT_WORD_BOUNDARY:
         tcode++;
         break;
   
       /* If we hit a bracket or a positive lookahead assertion, recurse to set        /* If we hit a bracket or a positive lookahead assertion, recurse to set
       bits from within the subpattern. If it can't find anything, we have to        bits from within the subpattern. If it can't find anything, we have to
       give up. If it finds some mandatory character(s), we are done for this        give up. If it finds some mandatory character(s), we are done for this
Line 636  do Line 957  do
       case OP_SBRA:        case OP_SBRA:
       case OP_CBRA:        case OP_CBRA:
       case OP_SCBRA:        case OP_SCBRA:
         case OP_BRAPOS:
         case OP_SBRAPOS:
         case OP_CBRAPOS:
         case OP_SCBRAPOS:
       case OP_ONCE:        case OP_ONCE:
         case OP_ONCE_NC:
       case OP_ASSERT:        case OP_ASSERT:
       rc = set_start_bits(tcode, start_bits, caseless, utf8, cd);        rc = set_start_bits(tcode, start_bits, utf, cd);
       if (rc == SSB_FAIL) return SSB_FAIL;        if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc;
       if (rc == SSB_DONE) try_next = FALSE; else        if (rc == SSB_DONE) try_next = FALSE; else
         {          {
         do tcode += GET(tcode, 1); while (*tcode == OP_ALT);          do tcode += GET(tcode, 1); while (*tcode == OP_ALT);
Line 662  do Line 988  do
       case OP_KET:        case OP_KET:
       case OP_KETRMAX:        case OP_KETRMAX:
       case OP_KETRMIN:        case OP_KETRMIN:
         case OP_KETRPOS:
       return SSB_CONTINUE;        return SSB_CONTINUE;
   
       /* Skip over callout */        /* Skip over callout */
Line 679  do Line 1006  do
       tcode += 1 + LINK_SIZE;        tcode += 1 + LINK_SIZE;
       break;        break;
   
       /* Skip over an option setting, changing the caseless flag */  
   
       case OP_OPT:  
       caseless = (tcode[1] & PCRE_CASELESS) != 0;  
       tcode += 2;  
       break;  
   
       /* BRAZERO does the bracket, but carries on. */        /* BRAZERO does the bracket, but carries on. */
   
       case OP_BRAZERO:        case OP_BRAZERO:
       case OP_BRAMINZERO:        case OP_BRAMINZERO:
       if (set_start_bits(++tcode, start_bits, caseless, utf8, cd) == SSB_FAIL)        case OP_BRAPOSZERO:
         return SSB_FAIL;        rc = set_start_bits(++tcode, start_bits, utf, cd);
         if (rc == SSB_FAIL || rc == SSB_UNKNOWN) return rc;
 /* =========================================================================  /* =========================================================================
       See the comment at the head of this function concerning the next line,        See the comment at the head of this function concerning the next line,
       which was an old fudge for the benefit of OS/2.        which was an old fudge for the benefit of OS/2.
Line 717  do Line 1038  do
       case OP_QUERY:        case OP_QUERY:
       case OP_MINQUERY:        case OP_MINQUERY:
       case OP_POSQUERY:        case OP_POSQUERY:
       tcode = set_table_bit(start_bits, tcode + 1, caseless, cd, utf8);        tcode = set_table_bit(start_bits, tcode + 1, FALSE, cd, utf);
         break;
   
         case OP_STARI:
         case OP_MINSTARI:
         case OP_POSSTARI:
         case OP_QUERYI:
         case OP_MINQUERYI:
         case OP_POSQUERYI:
         tcode = set_table_bit(start_bits, tcode + 1, TRUE, cd, utf);
       break;        break;
   
       /* Single-char upto sets the bit and tries the next */        /* Single-char upto sets the bit and tries the next */
Line 725  do Line 1055  do
       case OP_UPTO:        case OP_UPTO:
       case OP_MINUPTO:        case OP_MINUPTO:
       case OP_POSUPTO:        case OP_POSUPTO:
       tcode = set_table_bit(start_bits, tcode + 3, caseless, cd, utf8);        tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, FALSE, cd, utf);
       break;        break;
   
       /* At least one single char sets the bit and stops */        case OP_UPTOI:
         case OP_MINUPTOI:
         case OP_POSUPTOI:
         tcode = set_table_bit(start_bits, tcode + 1 + IMM2_SIZE, TRUE, cd, utf);
         break;
   
       case OP_EXACT:       /* Fall through */        /* At least one single char sets the bit and stops */
       tcode += 2;  
   
         case OP_EXACT:
         tcode += IMM2_SIZE;
         /* Fall through */
       case OP_CHAR:        case OP_CHAR:
       case OP_CHARNC:  
       case OP_PLUS:        case OP_PLUS:
       case OP_MINPLUS:        case OP_MINPLUS:
       case OP_POSPLUS:        case OP_POSPLUS:
       (void)set_table_bit(start_bits, tcode + 1, caseless, cd, utf8);        (void)set_table_bit(start_bits, tcode + 1, FALSE, cd, utf);
         try_next = FALSE;
         break;
   
         case OP_EXACTI:
         tcode += IMM2_SIZE;
         /* Fall through */
         case OP_CHARI:
         case OP_PLUSI:
         case OP_MINPLUSI:
         case OP_POSPLUSI:
         (void)set_table_bit(start_bits, tcode + 1, TRUE, cd, utf);
       try_next = FALSE;        try_next = FALSE;
       break;        break;
   
Line 749  do Line 1095  do
       identical. */        identical. */
   
       case OP_HSPACE:        case OP_HSPACE:
       SET_BIT(0x09);        SET_BIT(CHAR_HT);
       SET_BIT(0x20);        SET_BIT(CHAR_SPACE);
       if (utf8)  #ifdef SUPPORT_UTF
         if (utf)
         {          {
   #ifdef COMPILE_PCRE8
         SET_BIT(0xC2);  /* For U+00A0 */          SET_BIT(0xC2);  /* For U+00A0 */
         SET_BIT(0xE1);  /* For U+1680, U+180E */          SET_BIT(0xE1);  /* For U+1680, U+180E */
         SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */          SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */
         SET_BIT(0xE3);  /* For U+3000 */          SET_BIT(0xE3);  /* For U+3000 */
   #elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
           SET_BIT(0xA0);
           SET_BIT(0xFF);  /* For characters > 255 */
   #endif  /* COMPILE_PCRE[8|16|32] */
           }
         else
   #endif /* SUPPORT_UTF */
           {
   #ifndef EBCDIC
           SET_BIT(0xA0);
   #endif  /* Not EBCDIC */
   #if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
           SET_BIT(0xFF);  /* For characters > 255 */
   #endif  /* COMPILE_PCRE[16|32] */
         }          }
       else SET_BIT(0xA0);  
       try_next = FALSE;        try_next = FALSE;
       break;        break;
   
       case OP_ANYNL:        case OP_ANYNL:
       case OP_VSPACE:        case OP_VSPACE:
       SET_BIT(0x0A);        SET_BIT(CHAR_LF);
       SET_BIT(0x0B);        SET_BIT(CHAR_VT);
       SET_BIT(0x0C);        SET_BIT(CHAR_FF);
       SET_BIT(0x0D);        SET_BIT(CHAR_CR);
       if (utf8)  #ifdef SUPPORT_UTF
         if (utf)
         {          {
   #ifdef COMPILE_PCRE8
         SET_BIT(0xC2);  /* For U+0085 */          SET_BIT(0xC2);  /* For U+0085 */
         SET_BIT(0xE2);  /* For U+2028, U+2029 */          SET_BIT(0xE2);  /* For U+2028, U+2029 */
   #elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
           SET_BIT(CHAR_NEL);
           SET_BIT(0xFF);  /* For characters > 255 */
   #endif  /* COMPILE_PCRE[8|16|32] */
           }
         else
   #endif /* SUPPORT_UTF */
           {
           SET_BIT(CHAR_NEL);
   #if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
           SET_BIT(0xFF);  /* For characters > 255 */
   #endif
         }          }
       else SET_BIT(0x85);  
       try_next = FALSE;        try_next = FALSE;
       break;        break;
   
Line 792  do Line 1166  do
       try_next = FALSE;        try_next = FALSE;
       break;        break;
   
       /* The cbit_space table has vertical tab as whitespace; we have to        /* The cbit_space table has vertical tab as whitespace; we no longer
       ensure it is set as not whitespace. */        have to play fancy tricks because Perl added VT to its whitespace at
         release 5.18. PCRE added it at release 8.34. */
   
       case OP_NOT_WHITESPACE:        case OP_NOT_WHITESPACE:
       set_nottype_bits(start_bits, cbit_space, table_limit, cd);        set_nottype_bits(start_bits, cbit_space, table_limit, cd);
       start_bits[1] |= 0x08;  
       try_next = FALSE;        try_next = FALSE;
       break;        break;
   
       /* The cbit_space table has vertical tab as whitespace; we have to  
       not set it from the table. */  
   
       case OP_WHITESPACE:        case OP_WHITESPACE:
       c = start_bits[1];    /* Save in case it was already set */  
       set_type_bits(start_bits, cbit_space, table_limit, cd);        set_type_bits(start_bits, cbit_space, table_limit, cd);
       start_bits[1] = (start_bits[1] & ~0x08) | c;  
       try_next = FALSE;        try_next = FALSE;
       break;        break;
   
Line 831  do Line 1200  do
       break;        break;
   
       case OP_TYPEEXACT:        case OP_TYPEEXACT:
       tcode += 3;        tcode += 1 + IMM2_SIZE;
       break;        break;
   
       /* Zero or more repeats of character types set the bits and then        /* Zero or more repeats of character types set the bits and then
Line 840  do Line 1209  do
       case OP_TYPEUPTO:        case OP_TYPEUPTO:
       case OP_TYPEMINUPTO:        case OP_TYPEMINUPTO:
       case OP_TYPEPOSUPTO:        case OP_TYPEPOSUPTO:
       tcode += 2;               /* Fall through */        tcode += IMM2_SIZE;  /* Fall through */
   
       case OP_TYPESTAR:        case OP_TYPESTAR:
       case OP_TYPEMINSTAR:        case OP_TYPEMINSTAR:
Line 856  do Line 1225  do
         return SSB_FAIL;          return SSB_FAIL;
   
         case OP_HSPACE:          case OP_HSPACE:
         SET_BIT(0x09);          SET_BIT(CHAR_HT);
         SET_BIT(0x20);          SET_BIT(CHAR_SPACE);
         if (utf8)  #ifdef SUPPORT_UTF
           if (utf)
           {            {
   #ifdef COMPILE_PCRE8
           SET_BIT(0xC2);  /* For U+00A0 */            SET_BIT(0xC2);  /* For U+00A0 */
           SET_BIT(0xE1);  /* For U+1680, U+180E */            SET_BIT(0xE1);  /* For U+1680, U+180E */
           SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */            SET_BIT(0xE2);  /* For U+2000 - U+200A, U+202F, U+205F */
           SET_BIT(0xE3);  /* For U+3000 */            SET_BIT(0xE3);  /* For U+3000 */
   #elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
             SET_BIT(0xA0);
             SET_BIT(0xFF);  /* For characters > 255 */
   #endif  /* COMPILE_PCRE[8|16|32] */
           }            }
         else SET_BIT(0xA0);          else
   #endif /* SUPPORT_UTF */
   #ifndef EBCDIC
             SET_BIT(0xA0);
   #endif  /* Not EBCDIC */
         break;          break;
   
         case OP_ANYNL:          case OP_ANYNL:
         case OP_VSPACE:          case OP_VSPACE:
         SET_BIT(0x0A);          SET_BIT(CHAR_LF);
         SET_BIT(0x0B);          SET_BIT(CHAR_VT);
         SET_BIT(0x0C);          SET_BIT(CHAR_FF);
         SET_BIT(0x0D);          SET_BIT(CHAR_CR);
         if (utf8)  #ifdef SUPPORT_UTF
           if (utf)
           {            {
   #ifdef COMPILE_PCRE8
           SET_BIT(0xC2);  /* For U+0085 */            SET_BIT(0xC2);  /* For U+0085 */
           SET_BIT(0xE2);  /* For U+2028, U+2029 */            SET_BIT(0xE2);  /* For U+2028, U+2029 */
   #elif defined COMPILE_PCRE16 || defined COMPILE_PCRE32
             SET_BIT(CHAR_NEL);
             SET_BIT(0xFF);  /* For characters > 255 */
   #endif  /* COMPILE_PCRE16 */
           }            }
         else SET_BIT(0x85);          else
   #endif /* SUPPORT_UTF */
             SET_BIT(CHAR_NEL);
         break;          break;
   
         case OP_NOT_DIGIT:          case OP_NOT_DIGIT:
Line 890  do Line 1277  do
         set_type_bits(start_bits, cbit_digit, table_limit, cd);          set_type_bits(start_bits, cbit_digit, table_limit, cd);
         break;          break;
   
         /* The cbit_space table has vertical tab as whitespace; we have to          /* The cbit_space table has vertical tab as whitespace; we no longer
         ensure it gets set as not whitespace. */          have to play fancy tricks because Perl added VT to its whitespace at
           release 5.18. PCRE added it at release 8.34. */
   
         case OP_NOT_WHITESPACE:          case OP_NOT_WHITESPACE:
         set_nottype_bits(start_bits, cbit_space, table_limit, cd);          set_nottype_bits(start_bits, cbit_space, table_limit, cd);
         start_bits[1] |= 0x08;  
         break;          break;
   
         /* The cbit_space table has vertical tab as whitespace; we have to  
         avoid setting it. */  
   
         case OP_WHITESPACE:          case OP_WHITESPACE:
         c = start_bits[1];    /* Save in case it was already set */  
         set_type_bits(start_bits, cbit_space, table_limit, cd);          set_type_bits(start_bits, cbit_space, table_limit, cd);
         start_bits[1] = (start_bits[1] & ~0x08) | c;  
         break;          break;
   
         case OP_NOT_WORDCHAR:          case OP_NOT_WORDCHAR:
Line 925  do Line 1307  do
       with a value >= 0xc4 is a potentially valid starter because it starts a        with a value >= 0xc4 is a potentially valid starter because it starts a
       character with a value > 255. */        character with a value > 255. */
   
   #if defined SUPPORT_UTF || !defined COMPILE_PCRE8
         case OP_XCLASS:
         if ((tcode[1 + LINK_SIZE] & XCL_HASPROP) != 0)
           return SSB_FAIL;
         /* All bits are set. */
         if ((tcode[1 + LINK_SIZE] & XCL_MAP) == 0 && (tcode[1 + LINK_SIZE] & XCL_NOT) != 0)
           return SSB_FAIL;
   #endif
         /* Fall through */
   
       case OP_NCLASS:        case OP_NCLASS:
 #ifdef SUPPORT_UTF8  #if defined SUPPORT_UTF && defined COMPILE_PCRE8
       if (utf8)        if (utf)
         {          {
         start_bits[24] |= 0xf0;              /* Bits for 0xc4 - 0xc8 */          start_bits[24] |= 0xf0;              /* Bits for 0xc4 - 0xc8 */
         memset(start_bits+25, 0xff, 7);      /* Bits for 0xc9 - 0xff */          memset(start_bits+25, 0xff, 7);      /* Bits for 0xc9 - 0xff */
         }          }
 #endif  #endif
   #if defined COMPILE_PCRE16 || defined COMPILE_PCRE32
         SET_BIT(0xFF);                         /* For characters > 255 */
   #endif
       /* Fall through */        /* Fall through */
   
       case OP_CLASS:        case OP_CLASS:
         {          {
         tcode++;          pcre_uint8 *map;
   #if defined SUPPORT_UTF || !defined COMPILE_PCRE8
           map = NULL;
           if (*tcode == OP_XCLASS)
             {
             if ((tcode[1 + LINK_SIZE] & XCL_MAP) != 0)
               map = (pcre_uint8 *)(tcode + 1 + LINK_SIZE + 1);
             tcode += GET(tcode, 1);
             }
           else
   #endif
             {
             tcode++;
             map = (pcre_uint8 *)tcode;
             tcode += 32 / sizeof(pcre_uchar);
             }
   
         /* In UTF-8 mode, the bits in a bit map correspond to character          /* In UTF-8 mode, the bits in a bit map correspond to character
         values, not to byte values. However, the bit map we are constructing is          values, not to byte values. However, the bit map we are constructing is
Line 945  do Line 1355  do
         value is > 127. In fact, there are only two possible starting bytes for          value is > 127. In fact, there are only two possible starting bytes for
         characters in the range 128 - 255. */          characters in the range 128 - 255. */
   
 #ifdef SUPPORT_UTF8  #if defined SUPPORT_UTF || !defined COMPILE_PCRE8
         if (utf8)          if (map != NULL)
   #endif
           {            {
           for (c = 0; c < 16; c++) start_bits[c] |= tcode[c];  #if defined SUPPORT_UTF && defined COMPILE_PCRE8
           for (c = 128; c < 256; c++)            if (utf)
             {              {
             if ((tcode[c/8] && (1 << (c&7))) != 0)              for (c = 0; c < 16; c++) start_bits[c] |= map[c];
               for (c = 128; c < 256; c++)
               {                {
               int d = (c >> 6) | 0xc0;            /* Set bit for this starter */                if ((map[c/8] && (1 << (c&7))) != 0)
               start_bits[d/8] |= (1 << (d&7));    /* and then skip on to the */                  {
               c = (c & 0xc0) + 0x40 - 1;          /* next relevant character. */                  int d = (c >> 6) | 0xc0;            /* Set bit for this starter */
                   start_bits[d/8] |= (1 << (d&7));    /* and then skip on to the */
                   c = (c & 0xc0) + 0x40 - 1;          /* next relevant character. */
                   }
               }                }
             }              }
           }            else
   
         /* In non-UTF-8 mode, the two bit maps are completely compatible. */  
   
         else  
 #endif  #endif
           {              {
           for (c = 0; c < 32; c++) start_bits[c] |= tcode[c];              /* In non-UTF-8 mode, the two bit maps are completely compatible. */
               for (c = 0; c < 32; c++) start_bits[c] |= map[c];
               }
           }            }
   
         /* Advance past the bit map, and act on what follows */          /* Advance past the bit map, and act on what follows. For a zero
           minimum repeat, continue; otherwise stop processing. */
   
         tcode += 32;  
         switch (*tcode)          switch (*tcode)
           {            {
           case OP_CRSTAR:            case OP_CRSTAR:
           case OP_CRMINSTAR:            case OP_CRMINSTAR:
           case OP_CRQUERY:            case OP_CRQUERY:
           case OP_CRMINQUERY:            case OP_CRMINQUERY:
             case OP_CRPOSSTAR:
             case OP_CRPOSQUERY:
           tcode++;            tcode++;
           break;            break;
   
           case OP_CRRANGE:            case OP_CRRANGE:
           case OP_CRMINRANGE:            case OP_CRMINRANGE:
           if (((tcode[1] << 8) + tcode[2]) == 0) tcode += 5;            case OP_CRPOSRANGE:
             if (GET2(tcode, 1) == 0) tcode += 1 + 2 * IMM2_SIZE;
             else try_next = FALSE;              else try_next = FALSE;
           break;            break;
   
Line 1004  return yield; Line 1420  return yield;
   
   
   
   
   
 /*************************************************  /*************************************************
 *          Study a compiled expression           *  *          Study a compiled expression           *
 *************************************************/  *************************************************/
   
 /* This function is handed a compiled expression that it must study to produce  /* This function is handed a compiled expression that it must study to produce
 information that will speed up the matching. It returns a pcre_extra block  information that will speed up the matching. It returns a pcre[16]_extra block
 which then gets handed back to pcre_exec().  which then gets handed back to pcre_exec().
   
 Arguments:  Arguments:
Line 1018  Arguments: Line 1436  Arguments:
   errorptr  points to where to place error messages;    errorptr  points to where to place error messages;
             set NULL unless error              set NULL unless error
   
 Returns:    pointer to a pcre_extra block, with study_data filled in and the  Returns:    pointer to a pcre[16]_extra block, with study_data filled in and
               appropriate flags set;                the appropriate flags set;
             NULL on error or if no optimization possible              NULL on error or if no optimization possible
 */  */
   
   #if defined COMPILE_PCRE8
 PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION  PCRE_EXP_DEFN pcre_extra * PCRE_CALL_CONVENTION
 pcre_study(const pcre *external_re, int options, const char **errorptr)  pcre_study(const pcre *external_re, int options, const char **errorptr)
   #elif defined COMPILE_PCRE16
   PCRE_EXP_DEFN pcre16_extra * PCRE_CALL_CONVENTION
   pcre16_study(const pcre16 *external_re, int options, const char **errorptr)
   #elif defined COMPILE_PCRE32
   PCRE_EXP_DEFN pcre32_extra * PCRE_CALL_CONVENTION
   pcre32_study(const pcre32 *external_re, int options, const char **errorptr)
   #endif
 {  {
 int min;  int min;
 BOOL bits_set = FALSE;  BOOL bits_set = FALSE;
 uschar start_bits[32];  pcre_uint8 start_bits[32];
 pcre_extra *extra;  PUBL(extra) *extra = NULL;
 pcre_study_data *study;  pcre_study_data *study;
 const uschar *tables;  const pcre_uint8 *tables;
 uschar *code;  pcre_uchar *code;
 compile_data compile_block;  compile_data compile_block;
 const real_pcre *re = (const real_pcre *)external_re;  const REAL_PCRE *re = (const REAL_PCRE *)external_re;
   
   
 *errorptr = NULL;  *errorptr = NULL;
   
Line 1044  if (re == NULL || re->magic_number != MA Line 1471  if (re == NULL || re->magic_number != MA
   return NULL;    return NULL;
   }    }
   
   if ((re->flags & PCRE_MODE) == 0)
     {
   #if defined COMPILE_PCRE8
     *errorptr = "argument not compiled in 8 bit mode";
   #elif defined COMPILE_PCRE16
     *errorptr = "argument not compiled in 16 bit mode";
   #elif defined COMPILE_PCRE32
     *errorptr = "argument not compiled in 32 bit mode";
   #endif
     return NULL;
     }
   
 if ((options & ~PUBLIC_STUDY_OPTIONS) != 0)  if ((options & ~PUBLIC_STUDY_OPTIONS) != 0)
   {    {
   *errorptr = "unknown or incorrect option bit(s) set";    *errorptr = "unknown or incorrect option bit(s) set";
   return NULL;    return NULL;
   }    }
   
 code = (uschar *)re + re->name_table_offset +  code = (pcre_uchar *)re + re->name_table_offset +
   (re->name_count * re->name_entry_size);    (re->name_count * re->name_entry_size);
   
 /* For an anchored pattern, or an unanchored pattern that has a first char, or  /* For an anchored pattern, or an unanchored pattern that has a first char, or
Line 1060  seeking a list of starting bytes. */ Line 1499  seeking a list of starting bytes. */
 if ((re->options & PCRE_ANCHORED) == 0 &&  if ((re->options & PCRE_ANCHORED) == 0 &&
     (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0)      (re->flags & (PCRE_FIRSTSET|PCRE_STARTLINE)) == 0)
   {    {
     int rc;
   
   /* Set the character tables in the block that is passed around */    /* Set the character tables in the block that is passed around */
   
   tables = re->tables;    tables = re->tables;
   
   #if defined COMPILE_PCRE8
   if (tables == NULL)    if (tables == NULL)
     (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,      (void)pcre_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
     (void *)(&tables));      (void *)(&tables));
   #elif defined COMPILE_PCRE16
     if (tables == NULL)
       (void)pcre16_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
       (void *)(&tables));
   #elif defined COMPILE_PCRE32
     if (tables == NULL)
       (void)pcre32_fullinfo(external_re, NULL, PCRE_INFO_DEFAULT_TABLES,
       (void *)(&tables));
   #endif
   
   compile_block.lcc = tables + lcc_offset;    compile_block.lcc = tables + lcc_offset;
   compile_block.fcc = tables + fcc_offset;    compile_block.fcc = tables + fcc_offset;
Line 1074  if ((re->options & PCRE_ANCHORED) == 0 & Line 1526  if ((re->options & PCRE_ANCHORED) == 0 &
   
   /* See if we can find a fixed set of initial characters for the pattern. */    /* See if we can find a fixed set of initial characters for the pattern. */
   
   memset(start_bits, 0, 32 * sizeof(uschar));    memset(start_bits, 0, 32 * sizeof(pcre_uint8));
   bits_set = set_start_bits(code, start_bits,    rc = set_start_bits(code, start_bits, (re->options & PCRE_UTF8) != 0,
     (re->options & PCRE_CASELESS) != 0, (re->options & PCRE_UTF8) != 0,      &compile_block);
     &compile_block) == SSB_DONE;    bits_set = rc == SSB_DONE;
     if (rc == SSB_UNKNOWN)
       {
       *errorptr = "internal error: opcode not recognized";
       return NULL;
       }
   }    }
   
 /* Find the minimum length of subject string. */  /* Find the minimum length of subject string. */
   
 min = find_minlength(code, code, re->options);  switch(min = find_minlength(re, code, code, re->options, NULL))
     {
     case -2: *errorptr = "internal error: missing capturing bracket"; return NULL;
     case -3: *errorptr = "internal error: opcode not recognized"; return NULL;
     default: break;
     }
   
 /* Return NULL if no optimization is possible. */  /* If a set of starting bytes has been identified, or if the minimum length is
   greater than zero, or if JIT optimization has been requested, or if
   PCRE_STUDY_EXTRA_NEEDED is set, get a pcre[16]_extra block and a
   pcre_study_data block. The study data is put in the latter, which is pointed to
   by the former, which may also get additional data set later by the calling
   program. At the moment, the size of pcre_study_data is fixed. We nevertheless
   save it in a field for returning via the pcre_fullinfo() function so that if it
   becomes variable in the future, we don't have to change that code. */
   
   if (bits_set || min > 0 || (options & (
   #ifdef SUPPORT_JIT
       PCRE_STUDY_JIT_COMPILE | PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE |
       PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE |
   #endif
       PCRE_STUDY_EXTRA_NEEDED)) != 0)
     {
     extra = (PUBL(extra) *)(PUBL(malloc))
       (sizeof(PUBL(extra)) + sizeof(pcre_study_data));
     if (extra == NULL)
       {
       *errorptr = "failed to get memory";
       return NULL;
       }
   
 if (!bits_set && min < 0) return NULL;    study = (pcre_study_data *)((char *)extra + sizeof(PUBL(extra)));
     extra->flags = PCRE_EXTRA_STUDY_DATA;
     extra->study_data = study;
   
     study->size = sizeof(pcre_study_data);
     study->flags = 0;
   
     /* Set the start bits always, to avoid unset memory errors if the
     study data is written to a file, but set the flag only if any of the bits
     are set, to save time looking when none are. */
   
 /* Get a pcre_extra block and a pcre_study_data block. The study data is put in    if (bits_set)
 the latter, which is pointed to by the former, which may also get additional      {
 data set later by the calling program. At the moment, the size of      study->flags |= PCRE_STUDY_MAPPED;
 pcre_study_data is fixed. We nevertheless save it in a field for returning via      memcpy(study->start_bits, start_bits, sizeof(start_bits));
 the pcre_fullinfo() function so that if it becomes variable in the future, we      }
 don't have to change that code. */    else memset(study->start_bits, 0, 32 * sizeof(pcre_uint8));
   
 extra = (pcre_extra *)(pcre_malloc)  #ifdef PCRE_DEBUG
   (sizeof(pcre_extra) + sizeof(pcre_study_data));    if (bits_set)
       {
       pcre_uint8 *ptr = start_bits;
       int i;
   
 if (extra == NULL)      printf("Start bits:\n");
   {      for (i = 0; i < 32; i++)
   *errorptr = "failed to get memory";        printf("%3d: %02x%s", i * 8, *ptr++, ((i + 1) & 0x7) != 0? " " : "\n");
   return NULL;      }
   }  #endif
   
 study = (pcre_study_data *)((char *)extra + sizeof(pcre_extra));    /* Always set the minlength value in the block, because the JIT compiler
 extra->flags = PCRE_EXTRA_STUDY_DATA;    makes use of it. However, don't set the bit unless the length is greater than
 extra->study_data = study;    zero - the interpretive pcre_exec() and pcre_dfa_exec() needn't waste time
     checking the zero case. */
   
 study->size = sizeof(pcre_study_data);    if (min > 0)
 study->flags = 0;      {
       study->flags |= PCRE_STUDY_MINLEN;
       study->minlength = min;
       }
     else study->minlength = 0;
   
 if (bits_set)    /* If JIT support was compiled and requested, attempt the JIT compilation.
   {    If no starting bytes were found, and the minimum length is zero, and JIT
   study->flags |= PCRE_STUDY_MAPPED;    compilation fails, abandon the extra block and return NULL, unless
   memcpy(study->start_bits, start_bits, sizeof(start_bits));    PCRE_STUDY_EXTRA_NEEDED is set. */
   }  
   #ifdef SUPPORT_JIT
     extra->executable_jit = NULL;
     if ((options & PCRE_STUDY_JIT_COMPILE) != 0)
       PRIV(jit_compile)(re, extra, JIT_COMPILE);
     if ((options & PCRE_STUDY_JIT_PARTIAL_SOFT_COMPILE) != 0)
       PRIV(jit_compile)(re, extra, JIT_PARTIAL_SOFT_COMPILE);
     if ((options & PCRE_STUDY_JIT_PARTIAL_HARD_COMPILE) != 0)
       PRIV(jit_compile)(re, extra, JIT_PARTIAL_HARD_COMPILE);
   
 if (min >= 0)    if (study->flags == 0 && (extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) == 0 &&
   {        (options & PCRE_STUDY_EXTRA_NEEDED) == 0)
   study->flags |= PCRE_STUDY_MINLEN;      {
   study->minlength = min;  #if defined COMPILE_PCRE8
       pcre_free_study(extra);
   #elif defined COMPILE_PCRE16
       pcre16_free_study(extra);
   #elif defined COMPILE_PCRE32
       pcre32_free_study(extra);
   #endif
       extra = NULL;
       }
   #endif
   }    }
   
 return extra;  return extra;
 }  }
   
   
   /*************************************************
   *          Free the study data                   *
   *************************************************/
   
   /* This function frees the memory that was obtained by pcre_study().
   
   Argument:   a pointer to the pcre[16]_extra block
   Returns:    nothing
   */
   
   #if defined COMPILE_PCRE8
   PCRE_EXP_DEFN void
   pcre_free_study(pcre_extra *extra)
   #elif defined COMPILE_PCRE16
   PCRE_EXP_DEFN void
   pcre16_free_study(pcre16_extra *extra)
   #elif defined COMPILE_PCRE32
   PCRE_EXP_DEFN void
   pcre32_free_study(pcre32_extra *extra)
   #endif
   {
   if (extra == NULL)
     return;
   #ifdef SUPPORT_JIT
   if ((extra->flags & PCRE_EXTRA_EXECUTABLE_JIT) != 0 &&
        extra->executable_jit != NULL)
     PRIV(jit_free)(extra->executable_jit);
   #endif
   PUBL(free)(extra);
   }
   
 /* End of pcre_study.c */  /* End of pcre_study.c */

Removed from v.1.4  
changed lines
  Added in v.1.7


E-mail: