1 %{
2 /* GNU Mailutils -- a suite of utilities for electronic mail
3    Copyright (C) 2005-2021 Free Software Foundation, Inc.
4 
5    GNU Mailutils is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3, or (at your option)
8    any later version.
9 
10    GNU Mailutils is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
17 
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 
22 #include <mailutils/cctype.h>
23 #include <mimeview.h>
24 #include <grammar.h>
25 #include <regex.h>
26 
27 static void
yyprint(FILE * output,unsigned short toknum,YYSTYPE val)28 yyprint (FILE *output, unsigned short toknum, YYSTYPE val)
29 {
30   switch (toknum)
31     {
32     case TYPE:
33     case IDENT:
34     case STRING:
35       fprintf (output, "[%lu] %s", (unsigned long) val.string.len,
36 	       val.string.ptr);
37       break;
38 
39     case EOL:
40       fprintf (output, "\\n");
41       break;
42 
43     default:
44       if (mu_isprint (toknum))
45 	fprintf (output, "'%c'", toknum);
46       else
47 	fprintf (output, "tok(%d)", toknum);
48       break;
49     }
50 }
51 
52 #define YYPRINT yyprint
53 
54 static mu_list_t arg_list; /* For error recovery */
55 
56 #define L_OR  0
57 #define L_AND 1
58 
59 enum node_type
60   {
61     true_node,
62     functional_node,
63     binary_node,
64     negation_node,
65     suffix_node
66   };
67 
68 union argument
69 {
70   struct mimetypes_string *string;
71   unsigned number;
72   int c;
73   regex_t rx;
74 };
75 
76 typedef int (*builtin_t) (union argument *args);
77 
78 struct node
79 {
80   enum node_type type;
81   struct mu_locus_range loc;
82   union
83   {
84     struct
85     {
86       builtin_t fun;
87       union argument *args;
88     } function;
89     struct node *arg;
90     struct
91     {
92       int op;
93       struct node *arg1;
94       struct node *arg2;
95     } bin;
96     struct mimetypes_string suffix;
97   } v;
98 };
99 
100 static struct node *make_node (enum node_type type,
101 			       struct mu_locus_range const *loc);
102 static struct node *make_binary_node (int op,
103 				      struct node *left, struct node *rigth,
104 				      struct mu_locus_range const *loc);
105 static struct node *make_negation_node (struct node *p,
106 					struct mu_locus_range const *loc);
107 
108 static struct node *make_suffix_node (struct mimetypes_string *suffix,
109 				      struct mu_locus_range const *loc);
110 static struct node *make_functional_node (char *ident, mu_list_t list,
111 					  struct mu_locus_range const *loc);
112 
113 static int eval_rule (struct node *root);
114 
115 struct rule_tab
116 {
117   char *type;
118   int priority;
119   struct mu_locus_range loc;
120   struct node *node;
121 };
122 
123 static mu_list_t rule_list;
124 static size_t errors;
125 %}
126 
127 %locations
128 %expect 15
129 
130 %token <string> TYPE IDENT
131 %token <string> STRING
132 %token EOL BOGUS PRIORITY
133 
134 %left ','
135 %left '+'
136 
137 %type <string> arg
138 %type <list> arglist
139 %type <node> function stmt rule maybe_rule
140 %type <result> priority maybe_priority
141 
142 %union {
143   struct mimetypes_string string;
144   char *s;
145   mu_list_t list;
146   int result;
147   struct node *node;
148 }
149 
150 %%
151 
152 input    : list
153          ;
154 
155 list     : rule_line
156          | list EOL rule_line
157          ;
158 
159 rule_line: /* empty */
160          | TYPE maybe_rule maybe_priority
161            {
162 	     struct rule_tab *p = mimetypes_malloc (sizeof (*p));
163 	     if (!rule_list)
164 	       mu_list_create (&rule_list);
165 	     p->type = $1.ptr;
166 	     p->node = $2;
167 	     p->priority = $3;
168 	     mu_locus_point_copy (&p->loc.beg, &@1.beg);
169 	     mu_locus_point_copy (&p->loc.end, &@3.end);
170 #if 0
171 	     YY_LOCATION_PRINT (stderr, p->loc);
172 	     fprintf (stderr, ": rule %s\n", p->type);
173 #endif
174 	     mu_list_append (rule_list, p);
175 	   }
176 	 | BOGUS
177 	   {
178 	     YYERROR;
179 	   }
180          | error
181            {
182 	     errors++;
183 	     if (arg_list)
184 	       mu_list_destroy (&arg_list);
185 	     lex_next_rule ();
186 	     yyerrok;
187 	     yyclearin;
188 	   }
189          ;
190 
191 maybe_rule: /* empty */
192            {
193 	     $$ = make_node (true_node, &yylloc);
194 	   }
195          | rule
196 	 ;
197 
198 rule     : stmt
199          | rule rule %prec ','
200            {
201 	     struct mu_locus_range lr;
202 	     lr.beg = @1.beg;
203 	     lr.end = @2.end;
204 	     $$ = make_binary_node (L_OR, $1, $2, &lr);
205 	   }
206          | rule ',' rule
207            {
208 	     struct mu_locus_range lr;
209 	     lr.beg = @1.beg;
210 	     lr.end = @3.end;
211 	     $$ = make_binary_node (L_OR, $1, $3, &lr);
212 	   }
213          | rule '+' rule
214            {
215 	     struct mu_locus_range lr;
216 	     lr.beg = @1.beg;
217 	     lr.end = @3.end;
218 	     $$ = make_binary_node (L_AND, $1, $3, &lr);
219 	   }
220          ;
221 
222 stmt     : '!' stmt
223            {
224 	     $$ = make_negation_node ($2, &@2);
225 	   }
226          | '(' rule ')'
227            {
228 	     $$ = $2;
229 	   }
230          | STRING
231            {
232 	     $$ = make_suffix_node (&$1, &@1);
233 	   }
234          | function
235 	 | BOGUS
236 	   {
237 	     YYERROR;
238 	   }
239          ;
240 
241 priority : PRIORITY '(' arglist ')'
242            {
243 	     size_t count = 0;
244 	     struct mimetypes_string *arg;
245 
246 	     mu_list_count ($3, &count);
247 	     if (count != 1)
248 	       {
249 		 yyerror (_("priority takes single numberic argument"));
250 		 YYERROR;
251 	       }
252 	     mu_list_head ($3, (void**) &arg);
253 	     $$ = atoi (arg->ptr);
254 	     mu_list_destroy (&$3);
255 	   }
256          ;
257 
258 maybe_priority: /* empty */
259            {
260 	     $$ = 100;
261 	   }
262          | priority
263 	 ;
264 
265 function : IDENT '(' arglist ')'
266            {
267 	     struct mu_locus_range lr;
268 	     lr.beg = @1.beg;
269 	     lr.end = @4.end;
270 
271 	     $$ = make_functional_node ($1.ptr, $3, &lr);
272 	     if (!$$)
273 	       YYERROR;
274 	   }
275          ;
276 
277 arglist  : arg
278            {
279 	     mu_list_create (&arg_list);
280 	     $$ = arg_list;
281 	     mu_list_append ($$, mimetypes_string_dup (&$1));
282 	   }
283          | arglist ',' arg
284            {
285 	     mu_list_append ($1, mimetypes_string_dup (&$3));
286 	     $$ = $1;
287 	   }
288          ;
289 
290 arg      : STRING
291          | BOGUS
292            {
293 	     YYERROR;
294 	   }
295          ;
296 
297 %%
298 
299 int
300 mimetypes_parse (const char *name)
301 {
302   int rc;
303   if (mimetypes_open (name))
304     return 1;
305   yydebug = mu_debug_level_p (MU_DEBCAT_APP, MU_DEBUG_TRACE3);
306   rc = yyparse ();
307   mimetypes_close ();
308   return rc || errors;
309 }
310 
311 static struct node *
make_node(enum node_type type,struct mu_locus_range const * loc)312 make_node (enum node_type type, struct mu_locus_range const *loc)
313 {
314   struct node *p = mimetypes_malloc (sizeof *p);
315   p->type = type;
316   mu_locus_range_init (&p->loc);
317   mu_locus_range_copy (&p->loc, loc);
318   return p;
319 }
320 
321 static struct node *
make_binary_node(int op,struct node * left,struct node * right,struct mu_locus_range const * loc)322 make_binary_node (int op, struct node *left, struct node *right,
323 		  struct mu_locus_range const *loc)
324 {
325   struct node *node = make_node (binary_node, loc);
326 
327   node->v.bin.op = op;
328   node->v.bin.arg1 = left;
329   node->v.bin.arg2 = right;
330   return node;
331 }
332 
333 struct node *
make_negation_node(struct node * p,struct mu_locus_range const * loc)334 make_negation_node (struct node *p, struct mu_locus_range const *loc)
335 {
336   struct node *node = make_node (negation_node, loc);
337   node->v.arg = p;
338   return node;
339 }
340 
341 struct node *
make_suffix_node(struct mimetypes_string * suffix,struct mu_locus_range const * loc)342 make_suffix_node (struct mimetypes_string *suffix,
343 		  struct mu_locus_range const *loc)
344 {
345   struct node *node = make_node (suffix_node, loc);
346   node->v.suffix = *suffix;
347   return node;
348 }
349 
350 struct builtin_tab
351 {
352   char *name;
353   char *args;
354   builtin_t handler;
355 };
356 
357 /*        match("pattern")
358             Pattern match on filename
359 */
360 static int
b_match(union argument * args)361 b_match (union argument *args)
362 {
363   return fnmatch (args[0].string->ptr, mimeview_file, 0) == 0;
364 }
365 
366 /*       ascii(offset,length)
367             True if bytes are valid printable ASCII (CR, NL, TAB,
368             BS, 32-126)
369 */
370 #define ISASCII(c) ((c) &&\
371                     (strchr ("\n\r\t\b",c) \
372                      || (32<=((unsigned) c) && ((unsigned) c)<=126)))
373 static int
b_ascii(union argument * args)374 b_ascii (union argument *args)
375 {
376   int i;
377   int rc;
378 
379   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
380   if (rc)
381     {
382       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
383       return 0;
384     }
385 
386   for (i = 0; i < args[1].number; i++)
387     {
388       unsigned char c;
389       size_t n;
390 
391       rc = mu_stream_read (mimeview_stream, &c, 1, &n);
392       if (rc || n == 0)
393 	break;
394       if (!ISASCII (c))
395 	return 0;
396     }
397 
398   return 1;
399 }
400 
401 /*       printable(offset,length)
402             True if bytes are printable 8-bit chars (CR, NL, TAB,
403             BS, 32-126, 128-254)
404 */
405 #define ISPRINT(c) (ISASCII (c) \
406 		    || (128<=((unsigned) c) && ((unsigned) c)<=254))
407 static int
b_printable(union argument * args)408 b_printable (union argument *args)
409 {
410   int i;
411   int rc;
412 
413   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
414   if (rc)
415     {
416       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
417       return 0;
418     }
419 
420   for (i = 0; i < args[1].number; i++)
421     {
422       unsigned char c;
423       size_t n;
424 
425       rc = mu_stream_read (mimeview_stream, &c, 1, &n);
426       if (rc || n == 0)
427 	break;
428       if (!ISPRINT (c))
429 	return 0;
430     }
431   return 1;
432 }
433 
434 /*        string(offset,"string")
435             True if bytes are identical to string
436 */
437 static int
b_string(union argument * args)438 b_string (union argument *args)
439 {
440   struct mimetypes_string *str = args[1].string;
441   int i;
442   int rc;
443 
444   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
445   if (rc)
446     {
447       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
448       return 0;
449     }
450 
451   for (i = 0; i < str->len; i++)
452     {
453       char c;
454       size_t n;
455 
456       rc = mu_stream_read (mimeview_stream, &c, 1, &n);
457       if (rc || n == 0 || c != str->ptr[i])
458 	return 0;
459     }
460   return 1;
461 }
462 
463 /*        istring(offset,"string")
464             True if a case-insensitive comparison of the bytes is
465             identical
466 */
467 static int
b_istring(union argument * args)468 b_istring (union argument *args)
469 {
470   int i;
471   struct mimetypes_string *str = args[1].string;
472 
473   int rc;
474 
475   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
476   if (rc)
477     {
478       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
479       return 0;
480     }
481 
482   for (i = 0; i < str->len; i++)
483     {
484       char c;
485       size_t n;
486 
487       rc = mu_stream_read (mimeview_stream, &c, 1, &n);
488       if (rc || n == 0 || mu_tolower (c) != mu_tolower (str->ptr[i]))
489 	return 0;
490     }
491   return 1;
492 }
493 
494 int
compare_bytes(union argument * args,void * sample,void * buf,size_t size)495 compare_bytes (union argument *args, void *sample, void *buf, size_t size)
496 {
497   int rc;
498   size_t n;
499 
500   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
501   if (rc)
502     {
503       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
504       return 0;
505     }
506 
507   rc = mu_stream_read (mimeview_stream, buf, size, &n);
508   if (rc)
509     {
510       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", NULL, rc);
511       return 0;
512     }
513   else if (n != size)
514     return 0;
515   return memcmp (sample, buf, size) == 0;
516 }
517 
518 /*       char(offset,value)
519             True if byte is identical
520 */
521 static int
b_char(union argument * args)522 b_char (union argument *args)
523 {
524   char val = args[1].number;
525   char buf;
526   return compare_bytes (args, &val, &buf, sizeof (buf));
527 }
528 
529 /*        short(offset,value)
530             True if 16-bit integer is identical
531 	  FIXME: Byte order
532 */
533 static int
b_short(union argument * args)534 b_short (union argument *args)
535 {
536   uint16_t val = args[1].number;
537   uint16_t buf;
538   return compare_bytes (args, &val, &buf, sizeof (buf));
539 }
540 
541 /*        int(offset,value)
542             True if 32-bit integer is identical
543           FIXME: Byte order
544 */
545 static int
b_int(union argument * args)546 b_int (union argument *args)
547 {
548   uint32_t val = args[1].number;
549   uint32_t buf;
550   return compare_bytes (args, &val, &buf, sizeof (buf));
551 }
552 
553 /*        locale("string")
554             True if current locale matches string
555 */
556 static int
b_locale(union argument * args)557 b_locale (union argument *args)
558 {
559   abort (); /* FIXME */
560   return 0;
561 }
562 
563 /*        contains(offset,range,"string")
564             True if the range contains the string
565 */
566 static int
b_contains(union argument * args)567 b_contains (union argument *args)
568 {
569   size_t i, count;
570   char *buf;
571   struct mimetypes_string *str = args[2].string;
572   int rc;
573 
574   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
575   if (rc)
576     {
577       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
578       return 0;
579     }
580 
581   buf = mu_alloc (args[1].number);
582   rc = mu_stream_read (mimeview_stream, buf, args[1].number, &count);
583   if (rc)
584     {
585       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", NULL, rc);
586     }
587   else if (count > str->len)
588     for (i = 0; i <= count - str->len; i++)
589       if (buf[i] == str->ptr[0] && memcmp (buf + i, str->ptr, str->len) == 0)
590 	{
591 	  free (buf);
592 	  return 1;
593 	}
594   free (buf);
595   return 0;
596 }
597 
598 #define MIME_MAX_BUFFER 4096
599 
600 /*   regex(offset,"regex")		True if bytes match regular expression
601  */
602 static int
b_regex(union argument * args)603 b_regex (union argument *args)
604 {
605   size_t count;
606   int rc;
607   char buf[MIME_MAX_BUFFER];
608 
609   rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
610   if (rc)
611     {
612       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
613       return 0;
614     }
615 
616   rc = mu_stream_read (mimeview_stream, buf, sizeof buf - 1, &count);
617   if (rc)
618     {
619       mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", NULL, rc);
620       return 0;
621     }
622   buf[count] = 0;
623 
624   return regexec (&args[1].rx, buf, 0, NULL, 0) == 0;
625 }
626 
627 
628 static struct builtin_tab builtin_tab[] = {
629   { "match", "s", b_match },
630   { "ascii", "dd", b_ascii },
631   { "printable", "dd", b_printable },
632   { "regex", "dx", b_regex },
633   { "string", "ds", b_string },
634   { "istring", "ds", b_istring },
635   { "char", "dc", b_char },
636   { "short", "dd", b_short },
637   { "int", "dd", b_int },
638   { "locale", "s", b_locale },
639   { "contains", "dds", b_contains },
640   { NULL }
641 };
642 
643 struct node *
make_functional_node(char * ident,mu_list_t list,struct mu_locus_range const * loc)644 make_functional_node (char *ident, mu_list_t list,
645 		      struct mu_locus_range const *loc)
646 {
647   size_t count, i;
648   struct builtin_tab *p;
649   struct node *node;
650   union argument *args;
651   mu_iterator_t itr;
652   int rc;
653 
654   for (p = builtin_tab; ; p++)
655     {
656       if (!p->name)
657 	{
658 	  char *s;
659 	  mu_asprintf (&s, _("%s: unknown function"), ident);
660 	  yyerror (s);
661 	  free (s);
662 	  return NULL;
663 	}
664 
665       if (strcmp (ident, p->name) == 0)
666 	break;
667     }
668 
669   mu_list_count (list, &count);
670   i = strlen (p->args);
671 
672   if (count < i)
673     {
674       char *s;
675       mu_asprintf (&s, _("too few arguments in call to `%s'"), ident);
676       yyerror (s);
677       free (s);
678       return NULL;
679     }
680   else if (count > i)
681     {
682       char *s;
683       mu_asprintf (&s, _("too many arguments in call to `%s'"), ident);
684       yyerror (s);
685       free (s);
686       return NULL;
687     }
688 
689   args = mimetypes_malloc (count * sizeof *args);
690 
691   mu_list_get_iterator (list, &itr);
692   for (i = 0, mu_iterator_first (itr); !mu_iterator_is_done (itr);
693        mu_iterator_next (itr), i++)
694     {
695       struct mimetypes_string *data;
696       char *tmp;
697 
698       mu_iterator_current (itr, (void **)&data);
699       switch (p->args[i])
700 	{
701 	case 'd':
702 	  args[i].number = strtoul (data->ptr, &tmp, 0);
703 	  if (*tmp)
704 	    goto err;
705 	  break;
706 
707 	case 's':
708 	  args[i].string = data;
709 	  break;
710 
711 	case 'x':
712 	  {
713 	    char *s;
714 
715 	    rc = mu_c_str_unescape_trans (data->ptr,
716 					  "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v", &s);
717 	    if (rc)
718 	      {
719 		mu_diag_funcall (MU_DIAG_ERROR, "mu_c_str_unescape_trans",
720 				 data->ptr, rc);
721 		return NULL;
722 	      }
723 	    rc = regcomp (&args[i].rx, s, REG_EXTENDED|REG_NOSUB);
724 	    free (s);
725 	    if (rc)
726 	      {
727 		char errbuf[512];
728 		regerror (rc, &args[i].rx, errbuf, sizeof errbuf);
729 		yyerror (errbuf);
730 		return NULL;
731 	      }
732 	  }
733 	  break;
734 
735 	case 'c':
736 	  args[i].c = strtoul (data->ptr, &tmp, 0);
737 	  if (*tmp)
738 	    goto err;
739 	  break;
740 
741 	default:
742 	  abort ();
743 	}
744     }
745 
746   node = make_node (functional_node, loc);
747   node->v.function.fun = p->handler;
748   node->v.function.args = args;
749   return node;
750 
751  err:
752   {
753     char *s;
754     mu_asprintf (&s,
755 	         _("argument %lu has wrong type in call to `%s'"),
756 	         (unsigned long) i, ident);
757     yyerror (s);
758     free (s);
759     return NULL;
760   }
761 }
762 
763 static int
check_suffix(char * suf)764 check_suffix (char *suf)
765 {
766   char *p = strrchr (mimeview_file, '.');
767   if (!p)
768     return 0;
769   return strcmp (p+1, suf) == 0;
770 }
771 
772 void
mime_debug(int lev,struct mu_locus_range const * loc,char const * fmt,...)773 mime_debug (int lev, struct mu_locus_range const *loc, char const *fmt, ...)
774 {
775   if (mu_debug_level_p (MU_DEBCAT_APP, lev))
776     {
777       va_list ap;
778 
779       if (loc->beg.mu_col == 0)
780 	mu_debug_log_begin ("%s:%u", loc->beg.mu_file, loc->beg.mu_line);
781       else if (strcmp(loc->beg.mu_file, loc->end.mu_file))
782 	mu_debug_log_begin ("%s:%u.%u-%s:%u.%u",
783 			    loc->beg.mu_file,
784 			    loc->beg.mu_line, loc->beg.mu_col,
785 			    loc->end.mu_file,
786 			    loc->end.mu_line, loc->end.mu_col);
787       else if (loc->beg.mu_line != loc->end.mu_line)
788 	mu_debug_log_begin ("%s:%u.%u-%u.%u",
789 			    loc->beg.mu_file,
790 			    loc->beg.mu_line, loc->beg.mu_col,
791 			    loc->end.mu_line, loc->end.mu_col);
792       else if (loc->beg.mu_col != loc->end.mu_col)
793 	mu_debug_log_begin ("%s:%u.%u-%u",
794 			    loc->beg.mu_file,
795 			    loc->beg.mu_line, loc->beg.mu_col,
796 			    loc->end.mu_col);
797       else
798 	mu_debug_log_begin ("%s:%u.%u",
799 			    loc->beg.mu_file,
800 			    loc->beg.mu_line, loc->beg.mu_col);
801 
802       mu_stream_write (mu_strerr, ": ", 2, NULL);
803 
804       va_start (ap, fmt);
805       mu_stream_vprintf (mu_strerr, fmt, ap);
806       va_end (ap);
807       mu_debug_log_nl ();
808     }
809 }
810 
811 static int
eval_rule(struct node * root)812 eval_rule (struct node *root)
813 {
814   int result;
815 
816   switch (root->type)
817     {
818     case true_node:
819       result = 1;
820       break;
821 
822     case functional_node:
823       result = root->v.function.fun (root->v.function.args);
824       break;
825 
826     case binary_node:
827       result = eval_rule (root->v.bin.arg1);
828       switch (root->v.bin.op)
829 	{
830 	case L_OR:
831 	  if (!result)
832 	    result |= eval_rule (root->v.bin.arg2);
833 	  break;
834 
835 	case L_AND:
836 	  if (result)
837 	    result &= eval_rule (root->v.bin.arg2);
838 	  break;
839 
840 	default:
841 	  abort ();
842 	}
843       break;
844 
845     case negation_node:
846       result = !eval_rule (root->v.arg);
847       break;
848 
849     case suffix_node:
850       result = check_suffix (root->v.suffix.ptr);
851       break;
852 
853     default:
854       abort ();
855     }
856   mime_debug (MU_DEBUG_TRACE2, &root->loc, "result %s", result ? "true" : "false");
857   return result;
858 }
859 
860 static int
evaluate(void ** itmv,size_t itmc,void * call_data)861 evaluate (void **itmv, size_t itmc, void *call_data)
862 {
863   struct rule_tab *p = itmv[0];
864   if (eval_rule (p->node))
865     {
866       itmv[0] = p;
867       mime_debug (MU_DEBUG_TRACE1, &p->loc, "rule %s matches", p->type);
868       return MU_LIST_MAP_OK;
869     }
870   return MU_LIST_MAP_SKIP;
871 }
872 
873 static int
rule_cmp(const void * a,const void * b)874 rule_cmp (const void *a, const void *b)
875 {
876   struct rule_tab const *arule = a;
877   struct rule_tab const *brule = b;
878 
879   if (arule->priority == brule->priority)
880     {
881       if (arule->node->type == true_node
882 	  && brule->node->type != true_node)
883 	return 1;
884       else if (brule->node->type == true_node
885 	       && arule->node->type != true_node)
886 	return -1;
887       else
888 	return mu_c_strcasecmp (arule->type, brule->type);
889     }
890   return arule->priority - brule->priority;
891 }
892 
893 const char *
get_file_type()894 get_file_type ()
895 {
896   mu_list_t res = NULL;
897   const char *type = NULL;
898 
899   mu_list_map (rule_list, evaluate, NULL, 1, &res);
900   if (!mu_list_is_empty (res))
901     {
902       struct rule_tab *rule;
903       mu_list_sort (res, rule_cmp);
904       mu_list_head (res, (void**) &rule);
905       mime_debug (MU_DEBUG_TRACE0, &rule->loc, "selected rule %s", rule->type);
906       type = rule->type;
907     }
908   mu_list_destroy (&res);
909   return type;
910 }
911 
912