|
|
| 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 */ |