@@ -54,10 +54,30 @@ enum resfmt {
5454 RESFMT_CSV ,
5555};
5656
57+ enum filter_kind {
58+ FILTER_NAME ,
59+ FILTER_STAT ,
60+ };
61+
62+ enum operator_kind {
63+ OP_EQ , /* == or = */
64+ OP_NEQ , /* != or <> */
65+ OP_LT , /* < */
66+ OP_LE , /* <= */
67+ OP_GT , /* > */
68+ OP_GE , /* >= */
69+ };
70+
5771struct filter {
72+ enum filter_kind kind ;
73+ /* FILTER_NAME */
5874 char * any_glob ;
5975 char * file_glob ;
6076 char * prog_glob ;
77+ /* FILTER_STAT */
78+ enum operator_kind op ;
79+ int stat_id ;
80+ long value ;
6181};
6282
6383static struct env {
@@ -271,6 +291,8 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
271291
272292 for (i = 0 ; i < env .deny_filter_cnt ; i ++ ) {
273293 f = & env .deny_filters [i ];
294+ if (f -> kind != FILTER_NAME )
295+ continue ;
274296
275297 if (f -> any_glob && glob_matches (filename , f -> any_glob ))
276298 return false;
@@ -284,8 +306,10 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
284306
285307 for (i = 0 ; i < env .allow_filter_cnt ; i ++ ) {
286308 f = & env .allow_filters [i ];
287- allow_cnt ++ ;
309+ if (f -> kind != FILTER_NAME )
310+ continue ;
288311
312+ allow_cnt ++ ;
289313 if (f -> any_glob ) {
290314 if (glob_matches (filename , f -> any_glob ))
291315 return true;
@@ -306,11 +330,32 @@ static bool should_process_file_prog(const char *filename, const char *prog_name
306330 return allow_cnt == 0 ;
307331}
308332
333+ static struct {
334+ enum operator_kind op_kind ;
335+ const char * op_str ;
336+ } operators [] = {
337+ /* Order of these definitions matter to avoid situations like '<'
338+ * matching part of what is actually a '<>' operator. That is,
339+ * substrings should go last.
340+ */
341+ { OP_EQ , "==" },
342+ { OP_NEQ , "!=" },
343+ { OP_NEQ , "<>" },
344+ { OP_LE , "<=" },
345+ { OP_LT , "<" },
346+ { OP_GE , ">=" },
347+ { OP_GT , ">" },
348+ { OP_EQ , "=" },
349+ };
350+
351+ static bool parse_stat_id (const char * name , size_t len , int * id );
352+
309353static int append_filter (struct filter * * filters , int * cnt , const char * str )
310354{
311355 struct filter * f ;
312356 void * tmp ;
313357 const char * p ;
358+ int i ;
314359
315360 tmp = realloc (* filters , (* cnt + 1 ) * sizeof (* * filters ));
316361 if (!tmp )
@@ -320,6 +365,67 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
320365 f = & (* filters )[* cnt ];
321366 memset (f , 0 , sizeof (* f ));
322367
368+ /* First, let's check if it's a stats filter of the following form:
369+ * <stat><op><value, where:
370+ * - <stat> is one of supported numerical stats (verdict is also
371+ * considered numerical, failure == 0, success == 1);
372+ * - <op> is comparison operator (see `operators` definitions);
373+ * - <value> is an integer (or failure/success, or false/true as
374+ * special aliases for 0 and 1, respectively).
375+ * If the form doesn't match what user provided, we assume file/prog
376+ * glob filter.
377+ */
378+ for (i = 0 ; i < ARRAY_SIZE (operators ); i ++ ) {
379+ int id ;
380+ long val ;
381+ const char * end = str ;
382+ const char * op_str ;
383+
384+ op_str = operators [i ].op_str ;
385+ p = strstr (str , op_str );
386+ if (!p )
387+ continue ;
388+
389+ if (!parse_stat_id (str , p - str , & id )) {
390+ fprintf (stderr , "Unrecognized stat name in '%s'!\n" , str );
391+ return - EINVAL ;
392+ }
393+ if (id >= FILE_NAME ) {
394+ fprintf (stderr , "Non-integer stat is specified in '%s'!\n" , str );
395+ return - EINVAL ;
396+ }
397+
398+ p += strlen (op_str );
399+
400+ if (strcasecmp (p , "true" ) == 0 ||
401+ strcasecmp (p , "t" ) == 0 ||
402+ strcasecmp (p , "success" ) == 0 ||
403+ strcasecmp (p , "succ" ) == 0 ||
404+ strcasecmp (p , "s" ) == 0 ) {
405+ val = 1 ;
406+ } else if (strcasecmp (p , "false" ) == 0 ||
407+ strcasecmp (p , "f" ) == 0 ||
408+ strcasecmp (p , "failure" ) == 0 ||
409+ strcasecmp (p , "fail" ) == 0 ) {
410+ val = 0 ;
411+ } else {
412+ errno = 0 ;
413+ val = strtol (p , (char * * )& end , 10 );
414+ if (errno || end == p || * end != '\0' ) {
415+ fprintf (stderr , "Invalid integer value in '%s'!\n" , str );
416+ return - EINVAL ;
417+ }
418+ }
419+
420+ f -> kind = FILTER_STAT ;
421+ f -> stat_id = id ;
422+ f -> op = operators [i ].op_kind ;
423+ f -> value = val ;
424+
425+ * cnt += 1 ;
426+ return 0 ;
427+ }
428+
323429 /* File/prog filter can be specified either as '<glob>' or
324430 * '<file-glob>/<prog-glob>'. In the former case <glob> is applied to
325431 * both file and program names. This seems to be way more useful in
@@ -328,6 +434,7 @@ static int append_filter(struct filter **filters, int *cnt, const char *str)
328434 * name. But usually common <glob> seems to be the most useful and
329435 * ergonomic way.
330436 */
437+ f -> kind = FILTER_NAME ;
331438 p = strchr (str , '/' );
332439 if (!p ) {
333440 f -> any_glob = strdup (str );
@@ -1317,6 +1424,51 @@ static int handle_comparison_mode(void)
13171424 return 0 ;
13181425}
13191426
1427+ static bool is_stat_filter_matched (struct filter * f , const struct verif_stats * stats )
1428+ {
1429+ long value = stats -> stats [f -> stat_id ];
1430+
1431+ switch (f -> op ) {
1432+ case OP_EQ : return value == f -> value ;
1433+ case OP_NEQ : return value != f -> value ;
1434+ case OP_LT : return value < f -> value ;
1435+ case OP_LE : return value <= f -> value ;
1436+ case OP_GT : return value > f -> value ;
1437+ case OP_GE : return value >= f -> value ;
1438+ }
1439+
1440+ fprintf (stderr , "BUG: unknown filter op %d!\n" , f -> op );
1441+ return false;
1442+ }
1443+
1444+ static bool should_output_stats (const struct verif_stats * stats )
1445+ {
1446+ struct filter * f ;
1447+ int i , allow_cnt = 0 ;
1448+
1449+ for (i = 0 ; i < env .deny_filter_cnt ; i ++ ) {
1450+ f = & env .deny_filters [i ];
1451+ if (f -> kind != FILTER_STAT )
1452+ continue ;
1453+
1454+ if (is_stat_filter_matched (f , stats ))
1455+ return false;
1456+ }
1457+
1458+ for (i = 0 ; i < env .allow_filter_cnt ; i ++ ) {
1459+ f = & env .allow_filters [i ];
1460+ if (f -> kind != FILTER_STAT )
1461+ continue ;
1462+ allow_cnt ++ ;
1463+
1464+ if (is_stat_filter_matched (f , stats ))
1465+ return true;
1466+ }
1467+
1468+ /* if there are no stat allowed filters, pass everything through */
1469+ return allow_cnt == 0 ;
1470+ }
1471+
13201472static void output_prog_stats (void )
13211473{
13221474 const struct verif_stats * stats ;
@@ -1327,6 +1479,8 @@ static void output_prog_stats(void)
13271479 output_headers (RESFMT_TABLE_CALCLEN );
13281480 for (i = 0 ; i < env .prog_stat_cnt ; i ++ ) {
13291481 stats = & env .prog_stats [i ];
1482+ if (!should_output_stats (stats ))
1483+ continue ;
13301484 output_stats (stats , RESFMT_TABLE_CALCLEN , false);
13311485 last_stat_idx = i ;
13321486 }
@@ -1336,6 +1490,8 @@ static void output_prog_stats(void)
13361490 output_headers (env .out_fmt );
13371491 for (i = 0 ; i < env .prog_stat_cnt ; i ++ ) {
13381492 stats = & env .prog_stats [i ];
1493+ if (!should_output_stats (stats ))
1494+ continue ;
13391495 output_stats (stats , env .out_fmt , i == last_stat_idx );
13401496 }
13411497}
0 commit comments