1919#define TEST_TAG_EXPECT_FAILURE "comment:test_expect_failure"
2020#define TEST_TAG_EXPECT_SUCCESS "comment:test_expect_success"
2121#define TEST_TAG_EXPECT_MSG_PFX "comment:test_expect_msg="
22- #define TEST_TAG_EXPECT_REGEX_PFX "comment:test_expect_regex="
2322#define TEST_TAG_EXPECT_XLATED_PFX "comment:test_expect_xlated="
2423#define TEST_TAG_EXPECT_FAILURE_UNPRIV "comment:test_expect_failure_unpriv"
2524#define TEST_TAG_EXPECT_SUCCESS_UNPRIV "comment:test_expect_success_unpriv"
2625#define TEST_TAG_EXPECT_MSG_PFX_UNPRIV "comment:test_expect_msg_unpriv="
27- #define TEST_TAG_EXPECT_REGEX_PFX_UNPRIV "comment:test_expect_regex_unpriv="
2826#define TEST_TAG_EXPECT_XLATED_PFX_UNPRIV "comment:test_expect_xlated_unpriv="
2927#define TEST_TAG_LOG_LEVEL_PFX "comment:test_log_level="
3028#define TEST_TAG_PROG_FLAGS_PFX "comment:test_prog_flags="
@@ -55,8 +53,9 @@ enum mode {
5553
5654struct expect_msg {
5755 const char * substr ; /* substring match */
58- const char * regex_str ; /* regex-based match */
5956 regex_t regex ;
57+ bool is_regex ;
58+ bool on_next_line ;
6059};
6160
6261struct expected_msgs {
@@ -111,7 +110,7 @@ static void free_msgs(struct expected_msgs *msgs)
111110 int i ;
112111
113112 for (i = 0 ; i < msgs -> cnt ; i ++ )
114- if (msgs -> patterns [i ].regex_str )
113+ if (msgs -> patterns [i ].is_regex )
115114 regfree (& msgs -> patterns [i ].regex );
116115 free (msgs -> patterns );
117116 msgs -> patterns = NULL ;
@@ -132,12 +131,71 @@ static void free_test_spec(struct test_spec *spec)
132131 spec -> unpriv .name = NULL ;
133132}
134133
135- static int push_msg (const char * substr , const char * regex_str , struct expected_msgs * msgs )
134+ /* Compiles regular expression matching pattern.
135+ * Pattern has a special syntax:
136+ *
137+ * pattern := (<verbatim text> | regex)*
138+ * regex := "{{" <posix extended regular expression> "}}"
139+ *
140+ * In other words, pattern is a verbatim text with inclusion
141+ * of regular expressions enclosed in "{{" "}}" pairs.
142+ * For example, pattern "foo{{[0-9]+}}" matches strings like
143+ * "foo0", "foo007", etc.
144+ */
145+ static int compile_regex (const char * pattern , regex_t * regex )
146+ {
147+ char err_buf [256 ], buf [256 ] = {}, * ptr , * buf_end ;
148+ const char * original_pattern = pattern ;
149+ bool in_regex = false;
150+ int err ;
151+
152+ buf_end = buf + sizeof (buf );
153+ ptr = buf ;
154+ while (* pattern && ptr < buf_end - 2 ) {
155+ if (!in_regex && str_has_pfx (pattern , "{{" )) {
156+ in_regex = true;
157+ pattern += 2 ;
158+ continue ;
159+ }
160+ if (in_regex && str_has_pfx (pattern , "}}" )) {
161+ in_regex = false;
162+ pattern += 2 ;
163+ continue ;
164+ }
165+ if (in_regex ) {
166+ * ptr ++ = * pattern ++ ;
167+ continue ;
168+ }
169+ /* list of characters that need escaping for extended posix regex */
170+ if (strchr (".[]\\()*+?{}|^$" , * pattern )) {
171+ * ptr ++ = '\\' ;
172+ * ptr ++ = * pattern ++ ;
173+ continue ;
174+ }
175+ * ptr ++ = * pattern ++ ;
176+ }
177+ if (* pattern ) {
178+ PRINT_FAIL ("Regexp too long: '%s'\n" , original_pattern );
179+ return - EINVAL ;
180+ }
181+ if (in_regex ) {
182+ PRINT_FAIL ("Regexp has open '{{' but no closing '}}': '%s'\n" , original_pattern );
183+ return - EINVAL ;
184+ }
185+ err = regcomp (regex , buf , REG_EXTENDED | REG_NEWLINE );
186+ if (err != 0 ) {
187+ regerror (err , regex , err_buf , sizeof (err_buf ));
188+ PRINT_FAIL ("Regexp compilation error in '%s': '%s'\n" , buf , err_buf );
189+ return - EINVAL ;
190+ }
191+ return 0 ;
192+ }
193+
194+ static int __push_msg (const char * pattern , bool on_next_line , struct expected_msgs * msgs )
136195{
137- void * tmp ;
138- int regcomp_res ;
139- char error_msg [100 ];
140196 struct expect_msg * msg ;
197+ void * tmp ;
198+ int err ;
141199
142200 tmp = realloc (msgs -> patterns ,
143201 (1 + msgs -> cnt ) * sizeof (struct expect_msg ));
@@ -147,26 +205,38 @@ static int push_msg(const char *substr, const char *regex_str, struct expected_m
147205 }
148206 msgs -> patterns = tmp ;
149207 msg = & msgs -> patterns [msgs -> cnt ];
150-
151- if (substr ) {
152- msg -> substr = substr ;
153- msg -> regex_str = NULL ;
154- } else {
155- msg -> regex_str = regex_str ;
156- msg -> substr = NULL ;
157- regcomp_res = regcomp (& msg -> regex , regex_str , REG_EXTENDED |REG_NEWLINE );
158- if (regcomp_res != 0 ) {
159- regerror (regcomp_res , & msg -> regex , error_msg , sizeof (error_msg ));
160- PRINT_FAIL ("Regexp compilation error in '%s': '%s'\n" ,
161- regex_str , error_msg );
162- return - EINVAL ;
163- }
208+ msg -> on_next_line = on_next_line ;
209+ msg -> substr = pattern ;
210+ msg -> is_regex = false;
211+ if (strstr (pattern , "{{" )) {
212+ err = compile_regex (pattern , & msg -> regex );
213+ if (err )
214+ return err ;
215+ msg -> is_regex = true;
164216 }
165-
166217 msgs -> cnt += 1 ;
167218 return 0 ;
168219}
169220
221+ static int clone_msgs (struct expected_msgs * from , struct expected_msgs * to )
222+ {
223+ struct expect_msg * msg ;
224+ int i , err ;
225+
226+ for (i = 0 ; i < from -> cnt ; i ++ ) {
227+ msg = & from -> patterns [i ];
228+ err = __push_msg (msg -> substr , msg -> on_next_line , to );
229+ if (err )
230+ return err ;
231+ }
232+ return 0 ;
233+ }
234+
235+ static int push_msg (const char * substr , struct expected_msgs * msgs )
236+ {
237+ return __push_msg (substr , false, msgs );
238+ }
239+
170240static int parse_int (const char * str , int * val , const char * name )
171241{
172242 char * end ;
@@ -320,32 +390,22 @@ static int parse_test_spec(struct test_loader *tester,
320390 spec -> auxiliary = true;
321391 spec -> mode_mask |= UNPRIV ;
322392 } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_MSG_PFX ))) {
323- err = push_msg (msg , NULL , & spec -> priv .expect_msgs );
393+ err = push_msg (msg , & spec -> priv .expect_msgs );
324394 if (err )
325395 goto cleanup ;
326396 spec -> mode_mask |= PRIV ;
327397 } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_MSG_PFX_UNPRIV ))) {
328- err = push_msg (msg , NULL , & spec -> unpriv .expect_msgs );
329- if (err )
330- goto cleanup ;
331- spec -> mode_mask |= UNPRIV ;
332- } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_REGEX_PFX ))) {
333- err = push_msg (NULL , msg , & spec -> priv .expect_msgs );
334- if (err )
335- goto cleanup ;
336- spec -> mode_mask |= PRIV ;
337- } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_REGEX_PFX_UNPRIV ))) {
338- err = push_msg (NULL , msg , & spec -> unpriv .expect_msgs );
398+ err = push_msg (msg , & spec -> unpriv .expect_msgs );
339399 if (err )
340400 goto cleanup ;
341401 spec -> mode_mask |= UNPRIV ;
342402 } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_XLATED_PFX ))) {
343- err = push_msg (msg , NULL , & spec -> priv .expect_xlated );
403+ err = push_msg (msg , & spec -> priv .expect_xlated );
344404 if (err )
345405 goto cleanup ;
346406 spec -> mode_mask |= PRIV ;
347407 } else if ((msg = skip_dynamic_pfx (s , TEST_TAG_EXPECT_XLATED_PFX_UNPRIV ))) {
348- err = push_msg (msg , NULL , & spec -> unpriv .expect_xlated );
408+ err = push_msg (msg , & spec -> unpriv .expect_xlated );
349409 if (err )
350410 goto cleanup ;
351411 spec -> mode_mask |= UNPRIV ;
@@ -457,26 +517,10 @@ static int parse_test_spec(struct test_loader *tester,
457517 spec -> unpriv .execute = spec -> priv .execute ;
458518 }
459519
460- if (spec -> unpriv .expect_msgs .cnt == 0 ) {
461- for (i = 0 ; i < spec -> priv .expect_msgs .cnt ; i ++ ) {
462- struct expect_msg * msg = & spec -> priv .expect_msgs .patterns [i ];
463-
464- err = push_msg (msg -> substr , msg -> regex_str ,
465- & spec -> unpriv .expect_msgs );
466- if (err )
467- goto cleanup ;
468- }
469- }
470- if (spec -> unpriv .expect_xlated .cnt == 0 ) {
471- for (i = 0 ; i < spec -> priv .expect_xlated .cnt ; i ++ ) {
472- struct expect_msg * msg = & spec -> priv .expect_xlated .patterns [i ];
473-
474- err = push_msg (msg -> substr , msg -> regex_str ,
475- & spec -> unpriv .expect_xlated );
476- if (err )
477- goto cleanup ;
478- }
479- }
520+ if (spec -> unpriv .expect_msgs .cnt == 0 )
521+ clone_msgs (& spec -> priv .expect_msgs , & spec -> unpriv .expect_msgs );
522+ if (spec -> unpriv .expect_xlated .cnt == 0 )
523+ clone_msgs (& spec -> priv .expect_xlated , & spec -> unpriv .expect_xlated );
480524 }
481525
482526 spec -> valid = true;
@@ -542,7 +586,7 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
542586 struct expect_msg * msg = & msgs -> patterns [i ];
543587 const char * match = NULL ;
544588
545- if (msg -> substr ) {
589+ if (! msg -> is_regex ) {
546590 match = strstr (log , msg -> substr );
547591 if (match )
548592 log = match + strlen (msg -> substr );
@@ -562,8 +606,8 @@ static void validate_msgs(char *log_buf, struct expected_msgs *msgs,
562606 msg = & msgs -> patterns [j ];
563607 fprintf (stderr , "%s %s: '%s'\n" ,
564608 j < i ? "MATCHED " : "EXPECTED" ,
565- msg -> substr ? "SUBSTR " : " REGEX " ,
566- msg -> substr ?: msg -> regex_str );
609+ msg -> is_regex ? " REGEX " : "SUBSTR " ,
610+ msg -> substr );
567611 }
568612 return ;
569613 }
0 commit comments