--- /dev/null
+/*
+ tre-match-utils.h - TRE matcher helper definitions
+
+ This software is released under a BSD-style license.
+ See the file LICENSE for details and copyright.
+
+*/
+
+#include "tre-internal.h"
+
+#define str_source ((const tre_str_source*)string)
+
+#ifdef TRE_WCHAR
+
+#ifdef TRE_MULTIBYTE
+
+/* Wide character and multibyte support. */
+
+#ifdef TRE_STR_USER
+#define GET_NEXT_WCHAR() \
+ do { \
+ prev_c = next_c; \
+ if (type == STR_BYTE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = '\0'; \
+ else \
+ next_c = (unsigned char)(*str_byte++); \
+ } \
+ else if (type == STR_WIDE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = L'\0'; \
+ else \
+ next_c = *str_wide++; \
+ } \
+ else if (type == STR_MBS) \
+ { \
+ pos += pos_add_next; \
+ if (str_byte == NULL) \
+ next_c = L'\0'; \
+ else \
+ { \
+ size_t w; \
+ int max; \
+ if (len >= 0) \
+ max = len - pos; \
+ else \
+ max = 32; \
+ if (max <= 0) \
+ { \
+ next_c = L'\0'; \
+ pos_add_next = 1; \
+ } \
+ else \
+ { \
+ w = tre_mbrtowc_l(&next_c, str_byte, (size_t)max, &mbstate, \
+ tnfa->loc); \
+ if (w == (size_t)-1 || w == (size_t)-2) \
+ return REG_ILLSEQ; \
+ if (w == 0 && len >= 0) \
+ { \
+ pos_add_next = 1; \
+ next_c = 0; \
+ str_byte++; \
+ } \
+ else \
+ { \
+ pos_add_next = w; \
+ str_byte += w; \
+ } \
+ } \
+ } \
+ } \
+ else if (type == STR_USER) \
+ { \
+ pos += pos_add_next; \
+ str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \
+ str_source->context); \
+ } \
+ } while(/*CONSTCOND*/0)
+#else /* !TRE_STR_USER */
+#define GET_NEXT_WCHAR() \
+ do { \
+ prev_c = next_c; \
+ if (type == STR_BYTE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = '\0'; \
+ else \
+ next_c = (unsigned char)(*str_byte++); \
+ } \
+ else if (type == STR_WIDE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = L'\0'; \
+ else \
+ next_c = *str_wide++; \
+ } \
+ else if (type == STR_MBS) \
+ { \
+ pos += pos_add_next; \
+ if (str_byte == NULL) \
+ next_c = L'\0'; \
+ else \
+ { \
+ size_t w; \
+ int max; \
+ if (len >= 0) \
+ max = len - pos; \
+ else \
+ max = 32; \
+ if (max <= 0) \
+ { \
+ next_c = L'\0'; \
+ pos_add_next = 1; \
+ } \
+ else \
+ { \
+ w = tre_mbrtowc_l(&next_c, str_byte, (size_t)max, &mbstate, \
+ tnfa->loc); \
+ if (w == (size_t)-1 || w == (size_t)-2) \
+ return REG_ILLSEQ; \
+ if (w == 0 && len >= 0) \
+ { \
+ pos_add_next = 1; \
+ next_c = 0; \
+ str_byte++; \
+ } \
+ else \
+ { \
+ pos_add_next = w; \
+ str_byte += w; \
+ } \
+ } \
+ } \
+ } \
+ } while(/*CONSTCOND*/0)
+#endif /* !TRE_STR_USER */
+
+#else /* !TRE_MULTIBYTE */
+
+/* Wide character support, no multibyte support. */
+
+#ifdef TRE_STR_USER
+#define GET_NEXT_WCHAR() \
+ do { \
+ prev_c = next_c; \
+ if (type == STR_BYTE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = '\0'; \
+ else \
+ next_c = (unsigned char)(*str_byte++); \
+ } \
+ else if (type == STR_WIDE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = L'\0'; \
+ else \
+ next_c = *str_wide++; \
+ } \
+ else if (type == STR_USER) \
+ { \
+ pos += pos_add_next; \
+ str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \
+ str_source->context); \
+ } \
+ } while(/*CONSTCOND*/0)
+#else /* !TRE_STR_USER */
+#define GET_NEXT_WCHAR() \
+ do { \
+ prev_c = next_c; \
+ if (type == STR_BYTE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = '\0'; \
+ else \
+ next_c = (unsigned char)(*str_byte++); \
+ } \
+ else if (type == STR_WIDE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = L'\0'; \
+ else \
+ next_c = *str_wide++; \
+ } \
+ } while(/*CONSTCOND*/0)
+#endif /* !TRE_STR_USER */
+
+#endif /* !TRE_MULTIBYTE */
+
+#else /* !TRE_WCHAR */
+
+/* No wide character or multibyte support. */
+
+#ifdef TRE_STR_USER
+#define GET_NEXT_WCHAR() \
+ do { \
+ prev_c = next_c; \
+ if (type == STR_BYTE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = '\0'; \
+ else \
+ next_c = (unsigned char)(*str_byte++); \
+ } \
+ else if (type == STR_USER) \
+ { \
+ pos += pos_add_next; \
+ str_user_end = str_source->get_next_char(&next_c, &pos_add_next, \
+ str_source->context); \
+ } \
+ } while(/*CONSTCOND*/0)
+#else /* !TRE_STR_USER */
+#define GET_NEXT_WCHAR() \
+ do { \
+ prev_c = next_c; \
+ if (type == STR_BYTE) \
+ { \
+ pos++; \
+ if (len >= 0 && pos >= len) \
+ next_c = '\0'; \
+ else \
+ next_c = (unsigned char)(*str_byte++); \
+ } \
+ } while(/*CONSTCOND*/0)
+#endif /* !TRE_STR_USER */
+
+#endif /* !TRE_WCHAR */
+
+
+
+/* Assumes tre_tnfa_t *tnfa in scope */
+#define IS_WORD_CHAR(c) ((c) == L'_' || tre_isalnum_l(c, tnfa->loc))
+
+#define CHECK_ASSERTIONS(assertions) \
+ (((assertions & ASSERT_AT_BOL) \
+ && (pos > 0 || reg_notbol) \
+ && (prev_c != L'\n' || !reg_newline)) \
+ || ((assertions & ASSERT_AT_EOL) \
+ && (next_c != L'\0' || reg_noteol) \
+ && (next_c != L'\n' || !reg_newline)) \
+ || ((assertions & ASSERT_AT_BOW) \
+ && (IS_WORD_CHAR(prev_c) || !IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_EOW) \
+ && (!IS_WORD_CHAR(prev_c) || IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_WB) \
+ && (pos != 0 && next_c != L'\0' \
+ && IS_WORD_CHAR(prev_c) == IS_WORD_CHAR(next_c))) \
+ || ((assertions & ASSERT_AT_WB_NEG) \
+ && (pos == 0 || next_c == L'\0' \
+ || IS_WORD_CHAR(prev_c) != IS_WORD_CHAR(next_c))))
+
+#define CHECK_CHAR_CLASSES(trans_i, tnfa, eflags) \
+ ((trans_i->assertions & ASSERT_BRACKET_MATCH) \
+ && !tre_bracket_match(trans_i->u.bracket_match_list,(tre_cint_t)prev_c, \
+ tnfa))
+
+
+
+
+inline static int
+tre_tag_get(const tre_tag_t *tags, int i)
+{
+ tags += i;
+ return tags->count > 0 ? tags->value : -1;
+}
+
+inline static void
+tre_tag_set(tre_tag_t *tags, int i, int val, int touch)
+{
+ tags += i;
+ if (tags->count++ == 0)
+ tags->first = val;
+ tags->value = val;
+ tags->touch = touch;
+}
+
+inline static void
+tre_tag_reset(tre_tag_t *tags, int i)
+{
+ tags[i].count = 0;
+}
+
+inline static int
+tre_tag_touch_get(const tre_tag_t *tags, int i)
+{
+ return tags[i].touch;
+}
+
+#ifdef TRE_DEBUG
+inline static void
+tre_print_tags(const tre_tag_t *tags, int num_tags)
+{
+ int i;
+ for (i = 0; i < num_tags; i++, tags++)
+ {
+ switch(tags->count)
+ {
+ case 0:
+ DPRINT(("%d:(0,-1)", i));
+ break;
+ case 1:
+ DPRINT(("%d:(1,%d)", i, tags->first));
+ break;
+ default:
+ DPRINT(("%d:(%d,%d,%d)", i, tags->count, tags->first,
+ tags->value));
+ break;
+ }
+ if (i < (num_tags - 1))
+ DPRINT((" "));
+ }
+}
+
+inline static void
+tre_print_tags_all(const tre_tag_t *tags, int num_tags)
+{
+ int i;
+ for (i = 0; i < num_tags; i++, tags++)
+ {
+ switch(tags->count)
+ {
+ case 0:
+ DPRINT(("%d:(0,-1)/%d", i, tags->touch));
+ break;
+ case 1:
+ DPRINT(("%d:(1,%d)/%d", i, tags->first, tags->touch));
+ break;
+ default:
+ DPRINT(("%d:(%d,%d,%d)/%d", i, tags->count, tags->first,
+ tags->value, tags->touch));
+ break;
+ }
+ if (i < (num_tags - 1))
+ DPRINT((" "));
+ }
+}
+#endif /* TRE_DEBUG */
+
+/* Return < 0, = 0 or > 0 depending on how the start/end pairs of a minimal
+ * group between t1 and t2 compare (t1 loses if < 0, t1 wins if > 0) */
+inline static int
+tre_minimal_tag_order(int start, int end, const tre_tag_t *tags1,
+ const tre_tag_t *tags2)
+{
+ const tre_tag_t *t1, *t2;
+
+ t1 = tags1 + start;
+ t2 = tags2 + start;
+ /* We need both start tags to be set */
+ if (t1->count == 0 || t2->count == 0)
+ return 0;
+
+ /* The start tags must be equal */
+ if (t1->value != t2->value)
+ return 0;
+
+ t1 = tags1 + end;
+ t2 = tags2 + end;
+ /* For the end tags, we prefer set over unset, because unset means that
+ * the end tag is still growing */
+ if (t1->count == 0)
+ {
+ /* if t2 is set, t1 loses since it is unset */
+ if (t2->count != 0)
+ return -1;
+ }
+ /* if t2 not set, t1 wins since it is set */
+ else if (t2->count == 0)
+ return 1;
+
+ /* least current value wins */
+ return t2->value - t1->value;
+}
+
+/* Return < 0, = 0 or > 0 depending on how the i-th item of t1 and t2 compare
+ * (t1 loses if < 0, t1 wins if > 0) */
+inline static int
+tre_tag_order_1(int i, tre_tag_direction_t dir, const tre_tag_t *t1,
+ const tre_tag_t *t2)
+{
+ int diff;
+
+ t1 += i;
+ t2 += i;
+ switch (dir)
+ {
+ case TRE_TAG_MINIMIZE:
+ /* least current value wins (because tags are initialized to all zeros,
+ * unset wins over set; also, tre_minimal_tag_order() will have already
+ * been run, which checks for being unset) */
+ return t2->value - t1->value;
+
+ case TRE_TAG_MAXIMIZE:
+ /* prefer set */
+ if (t1->count == 0)
+ {
+ /* if neither t1 and t2 are set, try next tag */
+ if (t2->count == 0)
+ return 0;
+ /* t2 is set, t1 loses since it is unset */
+ return -1;
+ }
+ /* if t2 not set, t1 wins since it is set */
+ else if (t2->count == 0)
+ return 1;
+ /* greatest initial value wins */
+ if ((diff = t1->first - t2->first) != 0)
+ return diff;
+ /* least number of times the tag was set, wins */
+ if ((diff = t2->count - t1->count) != 0)
+ return diff;
+ /* if the tags were only set once, they only have initial values */
+ if (t1->count == 1)
+ return 0;
+ /* greatest current value wins */
+ return t1->value - t2->value;
+
+ case TRE_TAG_LEFT_MAXIMIZE:
+ /* prefer set */
+ if (t1->count == 0)
+ {
+ /* if neither t1 and t2 are set, try next tag */
+ if (t2->count == 0)
+ return 0;
+ /* t2 is set, t1 loses since it is unset */
+ return -1;
+ }
+ /* if t2 not set, t1 wins since it is set */
+ else if (t2->count == 0)
+ return 1;
+ /* least initial value wins */
+ if ((diff = t2->first - t1->first) != 0)
+ return diff;
+ /* least number of times the tag was set, wins */
+ if ((diff = t2->count - t1->count) != 0)
+ return diff;
+ /* if the tags were only set once, they only have initial values */
+ if (t1->count == 1)
+ return 0;
+ /* greatest current value wins */
+ return t1->value - t2->value;
+
+ default:
+ /* Shouldn't happen: only assert if TRE_DEBUG defined */
+ assert(0);
+ break;
+ }
+ return 0;
+}
+
+#ifdef TRE_DEBUG
+#define _MORE_DEBUGGING
+#endif /* TRE_DEBUG */
+
+/* Returns 1 if `t1' wins `t2', 0 otherwise. */
+inline static int
+#ifdef _MORE_DEBUGGING
+_tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+ const tre_tag_t *t1, const tre_tag_t *t2)
+#else /* !_MORE_DEBUGGING */
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+ const tre_tag_t *t1, const tre_tag_t *t2)
+#endif /* !_MORE_DEBUGGING */
+{
+ int i, ret;
+
+ for (i = 0; i < num_tags; i++)
+ {
+ if ((ret = tre_tag_order_1(i, tag_directions[i], t1, t2)) != 0)
+ return (ret > 0);
+ }
+ /* assert(0);*/
+ return 0;
+}
+
+#ifdef _MORE_DEBUGGING
+inline static int
+tre_tag_order(int num_tags, tre_tag_direction_t *tag_directions,
+ const tre_tag_t *t1, const tre_tag_t *t2)
+{
+ int ret = _tre_tag_order(num_tags, tag_directions, t1, t2);
+ DPRINT(("tre_tag_order: "));
+ tre_print_tags(t1, num_tags);
+ DPRINT((" %s ", ret ? "wins" : "doesn't win"));
+ tre_print_tags(t2, num_tags);
+ DPRINT(("\n"));
+ return ret;
+}
+#endif /* _MORE_DEBUGGING */
+
+#ifdef __LIBC__
+#include <xlocale_private.h>
+#else /* !__LIBC__ */
+#include <xlocale.h>
+#endif /* !__LIBC__ */
+
+int __collate_equiv_value(locale_t loc, const wchar_t *str, size_t len);
+
+inline static int
+tre_bracket_match(tre_bracket_match_list_t * __restrict list, tre_cint_t wc,
+ const tre_tnfa_t * __restrict tnfa)
+{
+ int match = 0;
+ int i;
+ tre_bracket_match_t *b;
+ tre_cint_t uc, lc;
+ int we, ue, le, got_equiv = 0;
+ int icase = ((tnfa->cflags & REG_ICASE) != 0);
+
+ DPRINT(("tre_bracket_match: %p, %d, %d\n", list, wc, icase));
+ if (icase)
+ {
+ if (tre_islower_l(wc, tnfa->loc))
+ {
+ lc = wc;
+ uc = tre_toupper_l(wc, tnfa->loc);
+ }
+ else if (tre_isupper_l(wc, tnfa->loc))
+ {
+ uc = wc;
+ lc = tre_tolower_l(wc, tnfa->loc);
+ }
+ else
+ {
+ icase = 0;
+ }
+ }
+ for (i = 0, b = list->bracket_matches; i < list->num_bracket_matches;
+ i++, b++)
+ {
+ switch (b->type)
+ {
+ case TRE_BRACKET_MATCH_TYPE_CHAR:
+ if (icase)
+ match = (b->value == uc || b->value == lc);
+ else
+ match = (b->value == wc);
+ break;
+ case TRE_BRACKET_MATCH_TYPE_RANGE_BEGIN:
+ {
+ tre_cint_t start = b->value, end;
+ if (++i >= list->num_bracket_matches ||
+ (++b)->type != TRE_BRACKET_MATCH_TYPE_RANGE_END)
+ {
+ DPRINT(("tre_bracket_match: no following range end\n"));
+ assert(0);
+ goto error;
+ }
+ end = b->value;
+ if (!got_equiv)
+ {
+ if (icase)
+ {
+ ue = __collate_equiv_value(tnfa->loc, &uc, 1);
+ le = __collate_equiv_value(tnfa->loc, &lc, 1);
+ }
+ else
+ we = __collate_equiv_value(tnfa->loc, &wc, 1);
+ got_equiv = 1;
+ }
+ if (icase)
+ match = ((start <= ue && ue <= end) ||
+ (start <= le && le <= end));
+ else
+ match = (start <= we && we <= end);
+ break;
+ }
+ case TRE_BRACKET_MATCH_TYPE_RANGE_END:
+ DPRINT(("tre_bracket_match: range end without preceeding start\n"));
+ assert(0);
+ break;
+ case TRE_BRACKET_MATCH_TYPE_CLASS:
+ if (icase)
+ match = (tre_isctype_l(uc, b->value, tnfa->loc) ||
+ tre_isctype_l(lc, b->value, tnfa->loc));
+ else
+ match = (tre_isctype_l(wc, b->value, tnfa->loc));
+ break;
+ case TRE_BRACKET_MATCH_TYPE_EQUIVALENCE:
+ if (!got_equiv)
+ {
+ if (icase)
+ {
+ ue = __collate_equiv_value(tnfa->loc, &uc, 1);
+ le = __collate_equiv_value(tnfa->loc, &lc, 1);
+ }
+ else
+ we = __collate_equiv_value(tnfa->loc, &wc, 1);
+ got_equiv = 1;
+ }
+ if (icase)
+ match = (b->value == ue || b->value == le);
+ else
+ match = (b->value == we);
+ break;
+ default:
+ DPRINT(("tre_bracket_match: unknown type %d\n", b->type));
+ assert(0);
+ break;
+ }
+ if (match)
+ break;
+ }
+error:
+ if (list->flags & TRE_BRACKET_MATCH_FLAG_NEGATE)
+ match = !match;
+ return match;
+}