#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include "mtint.h" static int eval_rule (struct node *root, struct filebuf const *file); /* match("pattern") Pattern match on filename */ static int b_match (union argument *args, struct filebuf const *fb) { return fnmatch (args[0].string.ptr, fb->name, 0) == 0; } static int filebuf_seek (struct filebuf const *fb, union argument const *args) { if (fseek (fb->fp, args[0].number, SEEK_SET)) { mimetypes_error ("fseek(%s,%ul,0)=%s", fb->name, args[0].number, strerror (errno)); return -1; } return 0; } /* ascii(offset,length) True if bytes are valid printable ASCII (CR, NL, TAB, BS, 32-126) */ #define ISASCII(c) ((c) &&\ (strchr ("\n\r\t\b",c) \ || (32<=((unsigned) c) && ((unsigned) c)<=126))) static int b_ascii (union argument *args, struct filebuf const *fb) { int i; if (filebuf_seek (fb, args)) return 0; for (i = 0; i < args[1].number; i++) { int c = fgetc (fb->fp); if (c == EOF) return 0; if (!ISASCII (c)) return 0; } return 1; } /* printable(offset,length) True if bytes are printable 8-bit chars (CR, NL, TAB, BS, 32-126, 128-254) */ #define ISPRINT(c) (ISASCII (c) \ || (128<=((unsigned) c) && ((unsigned) c)<=254)) static int b_printable (union argument *args, struct filebuf const *fb) { int i; if (filebuf_seek (fb, args)) return 0; for (i = 0; i < args[1].number; i++) { int c = fgetc (fb->fp); if (c == EOF) return 0; if (!ISPRINT (c)) return 0; } return 1; } /* string(offset,"string") True if bytes are identical to string */ static int b_string (union argument *args, struct filebuf const *fb) { struct mimetypes_string const *str = &args[1].string; int i; if (filebuf_seek (fb, args)) return 0; for (i = 0; i < str->len; i++) { int c = fgetc (fb->fp); if (c == EOF) return 0; if (c != str->ptr[i]) return 0; } return 1; } /* istring(offset,"string") True if a case-insensitive comparison of the bytes is identical */ static int b_istring (union argument *args, struct filebuf const *fb) { int i; struct mimetypes_string const *str = &args[1].string; if (filebuf_seek (fb, args)) return 0; for (i = 0; i < str->len; i++) { int c = fgetc (fb->fp); if (c == EOF) return 0; if (tolower (c) != tolower (str->ptr[i])) return 0; } return 1; } int compare_bytes (union argument *args, void *sample, void *buf, size_t size, struct filebuf const *fb) { if (filebuf_seek (fb, args)) return 0; if (fread (buf, size, 1, fb->fp) != 1) { if (ferror (fb->fp)) { mimetypes_error ("fread %zu bytes from %s: %s", size, fb->name, strerror (errno)); return 0; } } return memcmp (sample, buf, size) == 0; } /* char(offset,value) True if byte is identical */ static int b_char (union argument *args, struct filebuf const *fb) { char val = args[1].number; char buf; return compare_bytes (args, &val, &buf, sizeof (buf), fb); } /* short(offset,value) True if 16-bit integer is identical FIXME: Byte order */ static int b_short (union argument *args, struct filebuf const *fb) { uint16_t val = args[1].number; uint16_t buf; return compare_bytes (args, &val, &buf, sizeof (buf), fb); } /* int(offset,value) True if 32-bit integer is identical FIXME: Byte order */ static int b_int (union argument *args, struct filebuf const *fb) { uint32_t val = args[1].number; uint32_t buf; return compare_bytes (args, &val, &buf, sizeof (buf), fb); } /* locale("string") True if current locale matches string */ static int b_locale (union argument *args, struct filebuf const *fb) { abort (); /* FIXME */ return 0; } /* contains(offset,range,"string") True if the range contains the string */ static int b_contains (union argument *args, struct filebuf const *fb) { size_t i; size_t count; char *buf; struct mimetypes_string const *str = &args[2].string; if (filebuf_seek (fb, args)) return 0; buf = malloc (args[1].number); if (!buf) { mimetypes_error ("out of memory"); return 0; } count = fread (buf, 1, args[1].number, fb->fp); if (count == 0) { if (ferror (fb->fp)) mimetypes_error ("fread %lu bytes from %s: %s", args[1].number, fb->name, strerror (errno)); } else if (count > str->len) for (i = 0; i <= count - str->len; i++) if (buf[i] == str->ptr[0] && memcmp (buf + i, str->ptr, str->len) == 0) { free (buf); return 1; } free (buf); return 0; } #define MIME_MAX_BUFFER 4096 /* regex(offset,"regex") True if bytes match regular expression */ static int b_regex (union argument *args, struct filebuf const *fb) { size_t count; char buf[MIME_MAX_BUFFER]; if (filebuf_seek (fb, args)) return 0; count = fread (buf, 1, sizeof buf - 1, fb->fp); if (count == 0) { if (ferror (fb->fp)) mimetypes_error ("fread %zu bytes from %s: %s", sizeof buf - 1, fb->name, strerror (errno)); return 0; } buf[count] = 0; return regexec (&args[1].rx, buf, 0, NULL, 0) == 0; } static struct builtin_tab builtin_tab[] = { { "match", "s", b_match }, { "ascii", "dd", b_ascii }, { "printable", "dd", b_printable }, { "regex", "dx", b_regex }, { "string", "ds", b_string }, { "istring", "ds", b_istring }, { "char", "dc", b_char }, { "short", "dd", b_short }, { "int", "dd", b_int }, { "locale", "s", b_locale }, { "contains", "dds", b_contains }, { NULL } }; struct builtin_tab const * find_builtin (char const *ident) { struct builtin_tab *p; for (p = builtin_tab; p->name; p++) if (strcmp (ident, p->name) == 0) return p; return NULL; } static int check_suffix (char const *suf, struct filebuf const *fb) { char *p = strrchr (fb->name, '.'); if (!p) return 0; return strcmp (p+1, suf) == 0; } void mime_debug (struct locus_range const *loc, char const *fmt, ...) { char *p = getenv ("MIMETYPES_DEBUG_EVAL"); if (p && *p-'0') { va_list ap; if (loc->beg.col == 0) printf ("%s:%u", loc->beg.file, loc->beg.line); else if (strcmp (loc->beg.file, loc->end.file)) printf ("%s:%u.%u-%s:%u.%u", loc->beg.file, loc->beg.line, loc->beg.col, loc->end.file, loc->end.line, loc->end.col); else if (loc->beg.line != loc->end.line) printf ("%s:%u.%u-%u.%u", loc->beg.file, loc->beg.line, loc->beg.col, loc->end.line, loc->end.col); else if (loc->beg.col != loc->end.col) printf ("%s:%u.%u-%u", loc->beg.file, loc->beg.line, loc->beg.col, loc->end.col); else printf ("%s:%u.%u", loc->beg.file, loc->beg.line, loc->beg.col); printf (": "); va_start (ap, fmt); vprintf (fmt, ap); va_end (ap); putchar ('\n'); } } static int eval_rule (struct node *root, struct filebuf const *fb) { int result; switch (root->type) { case true_node: result = 1; break; case functional_node: result = root->v.function.fun (root->v.function.args, fb); break; case binary_node: result = eval_rule (root->v.bin.arg1, fb); switch (root->v.bin.op) { case L_OR: if (!result) result |= eval_rule (root->v.bin.arg2, fb); break; case L_AND: if (result) result &= eval_rule (root->v.bin.arg2, fb); break; default: abort (); } break; case negation_node: result = !eval_rule (root->v.arg, fb); break; case suffix_node: result = check_suffix (root->v.suffix.ptr, fb); break; default: abort (); } mime_debug (&root->loc, "result %s", result ? "true" : "false"); return result; } static int rule_cmp (struct rule const *arule, struct rule const *brule) { if (arule->priority == brule->priority) { if (arule->node->type == true_node && brule->node->type != true_node) return 1; else if (brule->node->type == true_node && arule->node->type != true_node) return -1; else return strcasecmp (arule->type, brule->type); } return arule->priority - brule->priority; } const char * get_file_type (char const *filename) { struct rule *r; struct rule *last = NULL; struct filebuf fb; fb.name = filename; fb.fp = fopen (filename, "r"); if (fb.fp == NULL) { mimetypes_error ("can't open %s: %s", filename, strerror (errno)); return NULL; } LL_FOREACH (&rule_list, r, link) { if (eval_rule (r->node, &fb)) { mime_debug (&r->loc, "rule %s matches", r->type); if (!last || rule_cmp (r, last) < 0) last = r; } } fclose (fb.fp); return last ? last->type : NULL; }