/* test.c: testing routines for regex.c. */ #include #ifdef STDC_HEADERS #include #else char *malloc (); char *realloc (); #endif /* Just to be complete, we make both the system V/ANSI and the BSD versions of the string functions available. */ #if USG || STDC_HEADERS #include #define index strchr #define rindex strrchr #define bcmp(s1, s2, len) memcmp ((s1), (s2), (len)) #define bcopy(from, to, len) memcpy ((to), (from), (len)) #define bzero(s, len) memset ((s), 0, (len)) #else #include #define strchr index #define strrchr rindex #ifndef NEED_MEMORY_H #define memcmp(s1, s2, n) bcmp ((s1), (s2), (n)) #define memcpy(to, from, len) bcopy ((from), (to), (len)) #endif extern char *strtok (); extern char *strstr (); #endif /* not USG or STDC_HEADERS */ /* SunOS 4.1 declares memchr in , not . I don't understand why. */ #if NEED_MEMORY_H #include #endif #include "test.h" #define BYTEWIDTH 8 extern void print_partial_compiled_pattern (); extern void print_compiled_pattern (); extern void print_double_string (); /* If nonzero, the results of every test are displayed. */ boolean verbose = false; /* If nonzero, don't do register testing. */ boolean omit_register_tests = true; /* Says whether the current test should match or fail to match. */ boolean test_should_match; static void set_all_registers (start0, end0, start1, end1, start2, end2, start3, end3, start4, end4, start5, end5, start6, end6, start7, end7, start8, end8, start9, end9, regs) int start0; int end0; int start1; int end1; int start2; int end2; int start3; int end3; int start4; int end4; int start5; int end5; int start6; int end6; int start7; int end7; int start8; int end8; int start9; int end9; struct re_registers *regs; { unsigned r; regs->start[0] = start0; regs->end[0] = end0; regs->start[1] = start1; regs->end[1] = end1; regs->start[2] = start2; regs->end[2] = end2; regs->start[3] = start3; regs->end[3] = end3; regs->start[4] = start4; regs->end[4] = end4; regs->start[5] = start5; regs->end[5] = end5; regs->start[6] = start6; regs->end[6] = end6; regs->start[7] = start7; regs->end[7] = end7; regs->start[8] = start8; regs->end[8] = end8; regs->start[9] = start9; regs->end[9] = end9; for (r = 10; r < regs->num_regs; r++) { regs->start[r] = -1; regs->end[r] = -1; } } /* Return the concatenation of S1 and S2. This would be a prime place to use varargs. */ char * concat (s1, s2) char *s1; char *s2; { char *answer = xmalloc (strlen (s1) + strlen (s2) + 1); strcpy (answer, s1); strcat (answer, s2); return answer; } #define OK_TO_SEARCH (nonconst_buf.fastmap_accurate && (str1 || str2)) /* We ignore the `can_be_null' argument. Should just be removed. */ void general_test (pattern_should_be_valid, match_whole_string, pat, str1, str2, start, range, end, correct_fastmap, correct_regs, can_be_null) unsigned pattern_should_be_valid; unsigned match_whole_string; const char *pat; char *str1, *str2; int start, range, end; char *correct_fastmap; struct re_registers *correct_regs; int can_be_null; { struct re_pattern_buffer nonconst_buf; struct re_pattern_buffer old_buf; struct re_registers regs; const char *r; char fastmap[1 << BYTEWIDTH]; unsigned *regs_correct = NULL; unsigned all_regs_correct = 1; boolean fastmap_internal_error = false; unsigned match = 0; unsigned match_1 = 0; unsigned match_2 = 0; unsigned invalid_pattern = 0; boolean internal_error_1 = false; boolean internal_error_2 = false; nonconst_buf.allocated = 8; nonconst_buf.buffer = xmalloc (nonconst_buf.allocated); nonconst_buf.fastmap = fastmap; nonconst_buf.translate = 0; assert (pat != NULL); r = re_compile_pattern (pat, strlen (pat), &nonconst_buf); /* Kludge: if we are doing POSIX testing, we really should have called regcomp, not re_compile_pattern. As it happens, the only way in which it matters is that re_compile_pattern sets the newline/anchor field for matching (part of what happens when REG_NEWLINE is given to regcomp). We have to undo that for POSIX matching. */ if (t == posix_basic_test || t == posix_extended_test) nonconst_buf.newline_anchor = 0; invalid_pattern = r != NULL; if (!r) { int r; if (!pattern_should_be_valid) printf ("\nShould have been an invalid pattern but wasn't:\n"); else { fastmap_internal_error = (re_compile_fastmap (&nonconst_buf) == -2); if (correct_fastmap) nonconst_buf.fastmap_accurate = memcmp (nonconst_buf.fastmap, correct_fastmap, 1 << BYTEWIDTH) == 0; if (OK_TO_SEARCH) { old_buf = nonconst_buf; old_buf.buffer = (unsigned char *) xmalloc (nonconst_buf.used); memcpy (old_buf.buffer, nonconst_buf.buffer, nonconst_buf.used); /* If only one string is null, call re_match or re_search, which is what the user would probably do. */ if (str1 == NULL && str2 != NULL || str2 == NULL && str1 != NULL) { char *the_str = str1 == NULL ? str2 : str1; match_1 = match_whole_string ? (r = re_match (&nonconst_buf, the_str, strlen (the_str), start, ®s)) == strlen (the_str) : (r = re_search (&nonconst_buf, the_str, strlen (the_str), start, range, ®s)) >= 0; if (r == -2) internal_error_1 = true; } else match_1 = 1; /* Also call with re_match_2 or re_search_2, as they might do this. (Also can check calling with either string1 or string2 or both null.) */ if (match_whole_string) { r = re_match_2 (&nonconst_buf, str1, SAFE_STRLEN (str1), str2, SAFE_STRLEN (str2), start, ®s, end); match_2 = r == SAFE_STRLEN (str1) + SAFE_STRLEN (str2); } else { r = re_search_2 (&nonconst_buf, str1, SAFE_STRLEN (str1), str2, SAFE_STRLEN (str2), start, range, ®s, end); match_2 = r >= 0; } if (r == -2) internal_error_2 = true; match = match_1 & match_2; if (correct_regs) { unsigned reg; if (regs_correct != NULL) free (regs_correct); regs_correct = (unsigned *) xmalloc (regs.num_regs * sizeof (unsigned)); for (reg = 0; reg < regs.num_regs && reg < correct_regs->num_regs; reg++) { regs_correct[reg] = (regs.start[reg] == correct_regs->start[reg] && regs.end[reg] == correct_regs->end[reg]) #ifdef EMPTY_REGS_CONFUSED /* There is confusion in the standard about the registers in some patterns which can match either the empty string or not match. For example, in `((a*))*' against the empty string, the two registers can either match the empty string (be 0/0), or not match (because of the outer *) (be -1/-1). (Or one can do one and one can do the other.) */ || (regs.start[reg] == -1 && regs.end[reg] == -1 && correct_regs->start[reg] == correct_regs->end[reg]) #endif ; all_regs_correct &= regs_correct[reg]; } } } /* OK_TO_SEARCH */ } } if (fastmap_internal_error) printf ("\n\nInternal error in re_compile_fastmap:"); if (internal_error_1) { if (!fastmap_internal_error) printf ("\n"); printf ("\nInternal error in re_match or re_search:"); } if (internal_error_2) { if (!internal_error_1) printf ("\n"); printf ("\nInternal error in re_match_2 or re_search_2:"); } if ((OK_TO_SEARCH && ((match && !test_should_match) || (!match && test_should_match)) || (correct_regs && !all_regs_correct)) || !nonconst_buf.fastmap_accurate || invalid_pattern || !pattern_should_be_valid || internal_error_1 || internal_error_2 || verbose) { if (OK_TO_SEARCH && match && !test_should_match) { printf ("\n\nMatched but shouldn't have:\n"); if (match_1) printf ("The single match/search succeeded.\n"); if (match_2) printf ("The double match/search succeeded.\n"); } else if (OK_TO_SEARCH && !match && test_should_match) { printf ("\n\nDidn't match but should have:\n"); if (!match_1) printf ("The single match/search failed.\n"); if (!match_2) printf ("The double match/search failed.\n"); } else if (invalid_pattern && pattern_should_be_valid) printf ("\n\nInvalid pattern (%s):\n", r); else if (!nonconst_buf.fastmap_accurate && pattern_should_be_valid) printf ("\n\nIncorrect fastmap:\n"); else if (OK_TO_SEARCH && correct_regs && !all_regs_correct) printf ("\n\nNot all registers were correct:\n"); else if (verbose) printf ("\n\nTest was OK:\n"); if ((!(invalid_pattern && !pattern_should_be_valid)) || verbose) printf (" Pattern: `%s'.\n", pat); if (pattern_should_be_valid || verbose || internal_error_1 || internal_error_2) { printf(" Strings: "); printf ("`%s' and ", str1 == NULL ? "NULL" : str1); printf ("`%s'.\n", str2 == NULL ? "NULL" : str2); if ((OK_TO_SEARCH || verbose || internal_error_1 || internal_error_2) && !invalid_pattern) { if (memcmp (old_buf.buffer, nonconst_buf.buffer, nonconst_buf.used) != 0 && !invalid_pattern) { printf(" (%s)\n", r ? r : "Valid regular expression"); printf ("\n Compiled pattern before matching: "); print_compiled_pattern (&old_buf); printf ("\n Compiled pattern after matching: "); } else printf ("\n Compiled pattern: "); print_compiled_pattern (&nonconst_buf); } if (correct_fastmap && (!nonconst_buf.fastmap_accurate || verbose)) { printf ("\n The fastmap should have been: "); print_fastmap (correct_fastmap); printf ("\n Fastmap: "); print_fastmap (fastmap); printf ("\n Compiled pattern before matching: "); print_compiled_pattern (&nonconst_buf); } if ((!all_regs_correct || verbose) && correct_regs) { unsigned this_reg; printf ("\n Incorrect registers:"); for (this_reg = 0; this_reg < regs.num_regs; this_reg++) { if (!regs_correct[this_reg]) { printf ("\n Register %d's start was %2d. ", this_reg, regs.start[this_reg]); printf ("\tIt should have been %d.\n", correct_regs->start[this_reg]); printf (" Register %d's end was %2d. ", this_reg, regs.end[this_reg]); printf ("\tIt should have been %d.\n", correct_regs->end[this_reg]); } } } } } if (nonconst_buf.buffer != NULL) free (nonconst_buf.buffer); if (OK_TO_SEARCH) { free (old_buf.buffer); if (correct_regs) free (regs_correct); } nonconst_buf.buffer = old_buf.buffer = NULL; regs_correct = NULL; regs.start = regs.end = NULL; } /* general_test */ void test_search_return (match_start_wanted, pattern, string) int match_start_wanted; const char *pattern; char *string; { struct re_pattern_buffer buf; char fastmap[1 << BYTEWIDTH]; const char *compile_return; int match_start; static num_times_called = 0; num_times_called++; buf.allocated = 1; buf.buffer = xmalloc (buf.allocated); assert (pattern != NULL); buf.translate = 0; compile_return = re_compile_pattern (pattern, strlen (pattern), &buf); if (compile_return) { printf ("\n\nInvalid pattern in test_match_start:\n"); printf ("%s\n", compile_return); } else { buf.fastmap = fastmap; match_start = re_search (&buf, string, strlen (string), 0, strlen (string), 0); if (match_start != match_start_wanted) printf ("\nWanted search to start at %d but started at %d.\n", match_start, match_start_wanted); } free (buf.buffer); buf.buffer = NULL; } #define SET_FASTMAP() \ { \ unsigned this_char; \ \ memset (correct_fastmap, invert, (1 << BYTEWIDTH)); \ \ for (this_char = 0; this_char < strlen (fastmap_string); this_char++)\ correct_fastmap[fastmap_string[this_char]] = !invert; \ correct_fastmap['\n'] = match_newline; \ } void test_fastmap (pat, fastmap_string, invert, match_newline) const char *pat; char *fastmap_string; unsigned invert; unsigned match_newline; { char correct_fastmap[(1 << BYTEWIDTH)]; SET_FASTMAP (); general_test (1, 0, pat, NULL, NULL, -1, 0, -1, correct_fastmap, 0, -1); } void test_fastmap_search (pat, str, fastmap_string, invert, match_newline, can_be_null, start0, end0) const char *pat; char *str; char *fastmap_string; unsigned invert; unsigned match_newline; int can_be_null; int start0; int end0; { char correct_fastmap[(1 << BYTEWIDTH)]; struct re_registers correct_regs; correct_regs.num_regs = RE_NREGS; correct_regs.start = (int *) xmalloc (RE_NREGS * sizeof (int)); correct_regs.end = (int *) xmalloc (RE_NREGS * sizeof (int)); set_all_registers (start0, end0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, &correct_regs); SET_FASTMAP (); general_test (1, 0, pat, str, NULL, 0, SAFE_STRLEN (str), SAFE_STRLEN (str), correct_fastmap, &correct_regs, can_be_null); free (correct_regs.start); free (correct_regs.end); } void test_all_registers (pat, str1, str2, start0, end0, start1, end1, start2, end2, start3, end3, start4, end4, start5, end5, start6, end6, start7, end7, start8, end8, start9, end9) char *pat; char *str1; char *str2; int start0; int end0; int start1; int end1; int start2; int end2; int start3; int end3; int start4; int end4; int start5; int end5; int start6; int end6; int start7; int end7; int start8; int end8; int start9; int end9; { struct re_registers correct_regs; if (omit_register_tests) return; correct_regs.num_regs = RE_NREGS; correct_regs.start = (int *) xmalloc (RE_NREGS * sizeof (int)); correct_regs.end = (int *) xmalloc (RE_NREGS * sizeof (int)); set_all_registers (start0, end0, start1, end1, start2, end2, start3, end3, start4, end4, start5, end5, start6, end6, start7, end7, start8, end8, start9, end9, &correct_regs); general_test (1, 0, pat, str1, str2, 0, SAFE_STRLEN (str1) + SAFE_STRLEN (str2), SAFE_STRLEN (str1) + SAFE_STRLEN (str2), NULL, &correct_regs, -1); free (correct_regs.start); free (correct_regs.end); } void invalid_pattern (error_code_expected, pattern) int error_code_expected; char *pattern; { regex_t pattern_buffer; int cflags = re_syntax_options == RE_SYNTAX_POSIX_EXTENDED || re_syntax_options == RE_SYNTAX_POSIX_MINIMAL_EXTENDED ? REG_EXTENDED : 0; test_compile (0, error_code_expected, pattern, &pattern_buffer, cflags); } void valid_pattern (pattern) char *pattern; { regex_t pattern_buffer; int cflags = re_syntax_options == RE_SYNTAX_POSIX_EXTENDED || re_syntax_options == RE_SYNTAX_POSIX_MINIMAL_EXTENDED ? REG_EXTENDED : 0; test_compile (1, 0, pattern, &pattern_buffer, cflags); } char * delimiters_to_ops (source, left_delimiter, right_delimiter) char *source; char left_delimiter; char right_delimiter; { static char *answer = NULL; char *tmp = NULL; boolean double_size = false; unsigned source_char; unsigned answer_char = 0; assert (source != NULL); switch (left_delimiter) { case '(': if (!(re_syntax_options & RE_NO_BK_PARENS)) double_size = true; break; case '{': if (!(re_syntax_options & RE_NO_BK_BRACES)) double_size = true; break; default: printf ("Found strange delimiter %c in delimiter_to_ops.\n", left_delimiter); printf ("The source was `%s'\n", source); exit (0); } if (answer == source) { tmp = (char *) xmalloc (strlen (source) + 1); strcpy (tmp, source); source = tmp; } if (answer) { free (answer); answer = NULL; } answer = (char *) xmalloc ((double_size ? strlen (source) << 1 : strlen (source)) + 1); if (!double_size) strcpy (answer, source); else { for (source_char = 0; source_char < strlen (source); source_char++) { if (source[source_char] == left_delimiter || source[source_char] == right_delimiter) answer[answer_char++] = '\\'; answer[answer_char++] = source[source_char]; } answer[answer_char] = 0; } return answer; } void print_pattern_info (pattern, pattern_buffer_ptr) const char *pattern; regex_t *pattern_buffer_ptr; { printf (" Pattern: `%s'.\n", pattern); printf (" Compiled pattern: "); print_compiled_pattern (pattern_buffer_ptr); } void valid_nonposix_pattern (pattern) char *pattern; { struct re_pattern_buffer nonconst_buf; nonconst_buf.allocated = 0; nonconst_buf.buffer = NULL; nonconst_buf.translate = NULL; assert (pattern != NULL); if (re_compile_pattern (pattern, strlen (pattern), &nonconst_buf)) { printf ("Couldn't compile the pattern.\n"); print_pattern_info (pattern, &nonconst_buf); } } void compile_and_print_pattern (pattern) char *pattern; { struct re_pattern_buffer nonconst_buf; nonconst_buf.allocated = 0; nonconst_buf.buffer = NULL; if (re_compile_pattern (pattern, strlen (pattern), &nonconst_buf)) printf ("Couldn't compile the pattern.\n"); print_pattern_info (pattern, &nonconst_buf); } void test_case_fold (pattern, string) const char *pattern; char* string; { struct re_pattern_buffer nonconst_buf; const char *ret; init_pattern_buffer (&nonconst_buf); nonconst_buf.translate = upcase; assert (pattern != NULL); ret = re_compile_pattern (pattern, strlen (pattern), &nonconst_buf); if (ret) { printf ("\nShould have been a valid pattern but wasn't.\n"); print_pattern_info (pattern, &nonconst_buf); } else { if (test_should_match && re_match (&nonconst_buf, string, strlen (string), 0, 0) != strlen (string)) { printf ("Match failed for case fold.\n"); printf (" Pattern: `%s'.\n", pattern); printf (" String: `%s'.\n", string == NULL ? "NULL" : string); } } } void test_match_n_times (n, pattern, string) unsigned n; char* pattern; char* string; { struct re_pattern_buffer buf; const char *r; unsigned match = 0; unsigned this_match; buf.allocated = 0; buf.buffer = NULL; buf.translate = 0; assert (pattern != NULL); r = re_compile_pattern (pattern, strlen (pattern), &buf); if (r) { printf ("Didn't compile.\n"); printf (" Pattern: %s.\n", pattern); } else { for (this_match = 1; this_match <= n; this_match++) match = (re_match (&buf, string, strlen (string), 0, 0) == strlen (string)); if (match && !test_should_match) printf ("\n\nMatched but shouldn't have:\n"); else if (!match && test_should_match) printf ("\n\nDidn't match but should have:\n"); if ((match && !test_should_match) || (!match && test_should_match)) { printf(" The string to match was: "); if (string) printf ("`%s' and ", string); else printf ("`'"); printf (" Pattern: %s.\n", pattern); printf (" Compiled pattern: %s.\n", pattern); print_compiled_pattern (&buf); } } } void test_match_2 (pat, str1, str2) const char *pat; char *str1; char *str2; { general_test (1, 1, pat, str1, str2, 0, 1, SAFE_STRLEN (str1) + SAFE_STRLEN (str2), NULL, 0, -1); } void test_match (pat, str) const char *pat; char *str; { test_match_2 (pat, str, NULL); test_match_2 (pat, NULL, str); }