2611 lines · 82.5 KB · C
RawDownload
/* * Regular Expression Engine *  * Copyright (c) 2017-2018 Fabrice Bellard * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */#include <stdlib.h>#include <stdio.h>#include <stdarg.h>#include <inttypes.h>#include <string.h>#include <assert.h> #include "cutils.h"#include "libregexp.h" /*  TODO:   - Add full unicode canonicalize rules for character ranges (not    really useful but needed for exact "ignorecase" compatibility).   - Add a lock step execution mode (=linear time execution guaranteed)    when the regular expression is "simple" i.e. no backreference nor    complicated lookahead. The opcodes are designed for this execution    model.*/ #if defined(TEST)#define DUMP_REOP#endif typedef enum {#define DEF(id, size) REOP_ ## id,#include "libregexp-opcode.h"#undef DEF    REOP_COUNT,} REOPCodeEnum; #define CAPTURE_COUNT_MAX 255#define STACK_SIZE_MAX 255 /* unicode code points */#define CP_LS   0x2028#define CP_PS   0x2029 #define TMP_BUF_SIZE 128 typedef struct {    DynBuf byte_code;    const uint8_t *buf_ptr;    const uint8_t *buf_end;    const uint8_t *buf_start;    int re_flags;    BOOL is_utf16;    BOOL ignore_case;    BOOL dotall;    int capture_count;    int total_capture_count; /* -1 = not computed yet */    int has_named_captures; /* -1 = don't know, 0 = no, 1 = yes */    void *opaque;    DynBuf group_names;    union {        char error_msg[TMP_BUF_SIZE];        char tmp_buf[TMP_BUF_SIZE];    } u;} REParseState; typedef struct {#ifdef DUMP_REOP    const char *name;#endif    uint8_t size;} REOpCode; static const REOpCode reopcode_info[REOP_COUNT] = {#ifdef DUMP_REOP#define DEF(id, size) { #id, size },#else#define DEF(id, size) { size },#endif#include "libregexp-opcode.h"#undef DEF}; #define RE_HEADER_FLAGS         0#define RE_HEADER_CAPTURE_COUNT 1#define RE_HEADER_STACK_SIZE    2 #define RE_HEADER_LEN 7 static inline int is_digit(int c) {    return c >= '0' && c <= '9';} /* insert 'len' bytes at position 'pos'. Return < 0 if error. */static int dbuf_insert(DynBuf *s, int pos, int len){    if (dbuf_realloc(s, s->size + len))        return -1;    memmove(s->buf + pos + len, s->buf + pos, s->size - pos);    s->size += len;    return 0;} /* canonicalize with the specific JS regexp rules */static uint32_t lre_canonicalize(uint32_t c, BOOL is_utf16){    uint32_t res[LRE_CC_RES_LEN_MAX];    int len;    if (is_utf16) {        if (likely(c < 128)) {            if (c >= 'A' && c <= 'Z')                c = c - 'A' + 'a';        } else {            lre_case_conv(res, c, 2);            c = res[0];        }    } else {        if (likely(c < 128)) {            if (c >= 'a' && c <= 'z')                c = c - 'a' + 'A';        } else {            /* legacy regexp: to upper case if single char >= 128 */            len = lre_case_conv(res, c, FALSE);            if (len == 1 && res[0] >= 128)                c = res[0];        }    }    return c;} static const uint16_t char_range_d[] = {    1,    0x0030, 0x0039 + 1,}; /* code point ranges for Zs,Zl or Zp property */static const uint16_t char_range_s[] = {    10,    0x0009, 0x000D + 1,    0x0020, 0x0020 + 1,    0x00A0, 0x00A0 + 1,    0x1680, 0x1680 + 1,    0x2000, 0x200A + 1,    /* 2028;LINE SEPARATOR;Zl;0;WS;;;;;N;;;;; */    /* 2029;PARAGRAPH SEPARATOR;Zp;0;B;;;;;N;;;;; */    0x2028, 0x2029 + 1,    0x202F, 0x202F + 1,    0x205F, 0x205F + 1,    0x3000, 0x3000 + 1,    /* FEFF;ZERO WIDTH NO-BREAK SPACE;Cf;0;BN;;;;;N;BYTE ORDER MARK;;;; */    0xFEFF, 0xFEFF + 1,}; BOOL lre_is_space(int c){    int i, n, low, high;    n = (countof(char_range_s) - 1) / 2;    for(i = 0; i < n; i++) {        low = char_range_s[2 * i + 1];        if (c < low)            return FALSE;        high = char_range_s[2 * i + 2];        if (c < high)            return TRUE;    }    return FALSE;} uint32_t const lre_id_start_table_ascii[4] = {    /* $ A-Z _ a-z */    0x00000000, 0x00000010, 0x87FFFFFE, 0x07FFFFFE}; uint32_t const lre_id_continue_table_ascii[4] = {    /* $ 0-9 A-Z _ a-z */    0x00000000, 0x03FF0010, 0x87FFFFFE, 0x07FFFFFE};  static const uint16_t char_range_w[] = {    4,    0x0030, 0x0039 + 1,    0x0041, 0x005A + 1,    0x005F, 0x005F + 1,    0x0061, 0x007A + 1,}; #define CLASS_RANGE_BASE 0x40000000 typedef enum {    CHAR_RANGE_d,    CHAR_RANGE_D,    CHAR_RANGE_s,    CHAR_RANGE_S,    CHAR_RANGE_w,    CHAR_RANGE_W,} CharRangeEnum; static const uint16_t *char_range_table[] = {    char_range_d,    char_range_s,    char_range_w,}; static int cr_init_char_range(REParseState *s, CharRange *cr, uint32_t c){    BOOL invert;    const uint16_t *c_pt;    int len, i;        invert = c & 1;    c_pt = char_range_table[c >> 1];    len = *c_pt++;    cr_init(cr, s->opaque, lre_realloc);    for(i = 0; i < len * 2; i++) {        if (cr_add_point(cr, c_pt[i]))            goto fail;    }    if (invert) {        if (cr_invert(cr))            goto fail;    }    return 0; fail:    cr_free(cr);    return -1;} static int cr_canonicalize(CharRange *cr){    CharRange a;    uint32_t pt[2];    int i, ret;     cr_init(&a, cr->mem_opaque, lre_realloc);    pt[0] = 'a';    pt[1] = 'z' + 1;    ret = cr_op(&a, cr->points, cr->len, pt, 2, CR_OP_INTER);    if (ret)        goto fail;    /* convert to upper case */    /* XXX: the generic unicode case would be much more complicated       and not really useful */    for(i = 0; i < a.len; i++) {        a.points[i] += 'A' - 'a';    }    /* Note: for simplicity we keep the lower case ranges */    ret = cr_union1(cr, a.points, a.len); fail:    cr_free(&a);    return ret;} #ifdef DUMP_REOPstatic __maybe_unused void lre_dump_bytecode(const uint8_t *buf,                                                     int buf_len){    int pos, len, opcode, bc_len, re_flags, i;    uint32_t val;        assert(buf_len >= RE_HEADER_LEN);     re_flags=  buf[0];    bc_len = get_u32(buf + 3);    assert(bc_len + RE_HEADER_LEN <= buf_len);    printf("flags: 0x%x capture_count=%d stack_size=%d\n",           re_flags, buf[1], buf[2]);    if (re_flags & LRE_FLAG_NAMED_GROUPS) {        const char *p;        p = (char *)buf + RE_HEADER_LEN + bc_len;        printf("named groups: ");        for(i = 1; i < buf[1]; i++) {            if (i != 1)                printf(",");            printf("<%s>", p);            p += strlen(p) + 1;        }        printf("\n");        assert(p == (char *)(buf + buf_len));    }    printf("bytecode_len=%d\n", bc_len);     buf += RE_HEADER_LEN;    pos = 0;    while (pos < bc_len) {        printf("%5u: ", pos);        opcode = buf[pos];        len = reopcode_info[opcode].size;        if (opcode >= REOP_COUNT) {            printf(" invalid opcode=0x%02x\n", opcode);            break;        }        if ((pos + len) > bc_len) {            printf(" buffer overflow (opcode=0x%02x)\n", opcode);            break;        }        printf("%s", reopcode_info[opcode].name);        switch(opcode) {        case REOP_char:            val = get_u16(buf + pos + 1);            if (val >= ' ' && val <= 126)                printf(" '%c'", val);            else                printf(" 0x%04x", val);            break;        case REOP_char32:            val = get_u32(buf + pos + 1);            if (val >= ' ' && val <= 126)                printf(" '%c'", val);            else                printf(" 0x%08x", val);            break;        case REOP_goto:        case REOP_split_goto_first:        case REOP_split_next_first:        case REOP_loop:        case REOP_lookahead:        case REOP_negative_lookahead:        case REOP_bne_char_pos:            val = get_u32(buf + pos + 1);            val += (pos + 5);            printf(" %u", val);            break;        case REOP_simple_greedy_quant:            printf(" %u %u %u %u",                   get_u32(buf + pos + 1) + (pos + 17),                   get_u32(buf + pos + 1 + 4),                   get_u32(buf + pos + 1 + 8),                   get_u32(buf + pos + 1 + 12));            break;        case REOP_save_start:        case REOP_save_end:        case REOP_back_reference:        case REOP_backward_back_reference:            printf(" %u", buf[pos + 1]);            break;        case REOP_save_reset:            printf(" %u %u", buf[pos + 1], buf[pos + 2]);            break;        case REOP_push_i32:            val = get_u32(buf + pos + 1);            printf(" %d", val);            break;        case REOP_range:            {                int n, i;                n = get_u16(buf + pos + 1);                len += n * 4;                for(i = 0; i < n * 2; i++) {                    val = get_u16(buf + pos + 3 + i * 2);                    printf(" 0x%04x", val);                }            }            break;        case REOP_range32:            {                int n, i;                n = get_u16(buf + pos + 1);                len += n * 8;                for(i = 0; i < n * 2; i++) {                    val = get_u32(buf + pos + 3 + i * 4);                    printf(" 0x%08x", val);                }            }            break;        default:            break;        }        printf("\n");        pos += len;    }}#endif static void re_emit_op(REParseState *s, int op){    dbuf_putc(&s->byte_code, op);} /* return the offset of the u32 value */static int re_emit_op_u32(REParseState *s, int op, uint32_t val){    int pos;    dbuf_putc(&s->byte_code, op);    pos = s->byte_code.size;    dbuf_put_u32(&s->byte_code, val);    return pos;} static int re_emit_goto(REParseState *s, int op, uint32_t val){    int pos;    dbuf_putc(&s->byte_code, op);    pos = s->byte_code.size;    dbuf_put_u32(&s->byte_code, val - (pos + 4));    return pos;} static void re_emit_op_u8(REParseState *s, int op, uint32_t val){    dbuf_putc(&s->byte_code, op);    dbuf_putc(&s->byte_code, val);} static void re_emit_op_u16(REParseState *s, int op, uint32_t val){    dbuf_putc(&s->byte_code, op);    dbuf_put_u16(&s->byte_code, val);} static int __attribute__((format(printf, 2, 3))) re_parse_error(REParseState *s, const char *fmt, ...){    va_list ap;    va_start(ap, fmt);    vsnprintf(s->u.error_msg, sizeof(s->u.error_msg), fmt, ap);    va_end(ap);    return -1;} static int re_parse_out_of_memory(REParseState *s){    return re_parse_error(s, "out of memory");} /* If allow_overflow is false, return -1 in case of   overflow. Otherwise return INT32_MAX. */static int parse_digits(const uint8_t **pp, BOOL allow_overflow){    const uint8_t *p;    uint64_t v;    int c;        p = *pp;    v = 0;    for(;;) {        c = *p;        if (c < '0' || c > '9')            break;        v = v * 10 + c - '0';        if (v >= INT32_MAX) {            if (allow_overflow)                v = INT32_MAX;            else                return -1;        }        p++;    }    *pp = p;    return v;} static int re_parse_expect(REParseState *s, const uint8_t **pp, int c){    const uint8_t *p;    p = *pp;    if (*p != c)        return re_parse_error(s, "expecting '%c'", c);    p++;    *pp = p;    return 0;} /* Parse an escape sequence, *pp points after the '\':   allow_utf16 value:   0 : no UTF-16 escapes allowed   1 : UTF-16 escapes allowed   2 : UTF-16 escapes allowed and escapes of surrogate pairs are   converted to a unicode character (unicode regexp case).    Return the unicode char and update *pp if recognized,   return -1 if malformed escape,   return -2 otherwise. */int lre_parse_escape(const uint8_t **pp, int allow_utf16){    const uint8_t *p;    uint32_t c;     p = *pp;    c = *p++;    switch(c) {    case 'b':

Lines 1–500 of 2611.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t