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