1"""
2This module is based on a rox module (LGPL):
3
4http://cvs.sourceforge.net/viewcvs.py/rox/ROX-Lib2/python/rox/mime.py?rev=1.21&view=log
5
6This module provides access to the shared MIME database.
7
8types is a dictionary of all known MIME types, indexed by the type name, e.g.
9types['application/x-python']
10
11Applications can install information about MIME types by storing an
12XML file as <MIME>/packages/<application>.xml and running the
13update-mime-database command, which is provided by the freedesktop.org
14shared mime database package.
15
16See http://www.freedesktop.org/standards/shared-mime-info-spec/ for
17information about the format of these files.
18
19(based on version 0.13)
20"""
21
22import os
23import re
24import stat
25import sys
26import fnmatch
27
28from xdg import BaseDirectory
29import xdg.Locale
30
31from xml.dom import minidom, XML_NAMESPACE
32from collections import defaultdict
33
34FREE_NS = 'http://www.freedesktop.org/standards/shared-mime-info'
35
36types = {}      # Maps MIME names to type objects
37
38exts = None     # Maps extensions to types
39globs = None    # List of (glob, type) pairs
40literals = None # Maps liternal names to types
41magic = None
42
43PY3 = (sys.version_info[0] >= 3)
44
45def _get_node_data(node):
46    """Get text of XML node"""
47    return ''.join([n.nodeValue for n in node.childNodes]).strip()
48
49def lookup(media, subtype = None):
50    """Get the MIMEtype object for the given type.
51
52    This remains for backwards compatibility; calling MIMEtype now does
53    the same thing.
54
55    The name can either be passed as one part ('text/plain'), or as two
56    ('text', 'plain').
57    """
58    return MIMEtype(media, subtype)
59
60class MIMEtype(object):
61    """Class holding data about a MIME type.
62
63    Calling the class will return a cached instance, so there is only one
64    instance for each MIME type. The name can either be passed as one part
65    ('text/plain'), or as two ('text', 'plain').
66    """
67    def __new__(cls, media, subtype=None):
68        if subtype is None and '/' in media:
69            media, subtype = media.split('/', 1)
70        assert '/' not in subtype
71        media = media.lower()
72        subtype = subtype.lower()
73
74        try:
75            return types[(media, subtype)]
76        except KeyError:
77            mtype = super(MIMEtype, cls).__new__(cls)
78            mtype._init(media, subtype)
79            types[(media, subtype)] = mtype
80            return mtype
81
82    # If this is done in __init__, it is automatically called again each time
83    # the MIMEtype is returned by __new__, which we don't want. So we call it
84    # explicitly only when we construct a new instance.
85    def _init(self, media, subtype):
86        self.media = media
87        self.subtype = subtype
88        self._comment = None
89
90    def _load(self):
91        "Loads comment for current language. Use get_comment() instead."
92        resource = os.path.join('mime', self.media, self.subtype + '.xml')
93        for path in BaseDirectory.load_data_paths(resource):
94            doc = minidom.parse(path)
95            if doc is None:
96                continue
97            for comment in doc.documentElement.getElementsByTagNameNS(FREE_NS, 'comment'):
98                lang = comment.getAttributeNS(XML_NAMESPACE, 'lang') or 'en'
99                goodness = 1 + (lang in xdg.Locale.langs)
100                if goodness > self._comment[0]:
101                    self._comment = (goodness, _get_node_data(comment))
102                if goodness == 2: return
103
104    # FIXME: add get_icon method
105    def get_comment(self):
106        """Returns comment for current language, loading it if needed."""
107        # Should we ever reload?
108        if self._comment is None:
109            self._comment = (0, str(self))
110            self._load()
111        return self._comment[1]
112
113    def canonical(self):
114        """Returns the canonical MimeType object if this is an alias."""
115        update_cache()
116        s = str(self)
117        if s in aliases:
118            return lookup(aliases[s])
119        return self
120
121    def inherits_from(self):
122        """Returns a set of Mime types which this inherits from."""
123        update_cache()
124        return set(lookup(t) for t in inheritance[str(self)])
125
126    def __str__(self):
127        return self.media + '/' + self.subtype
128
129    def __repr__(self):
130        return 'MIMEtype(%r, %r)' % (self.media, self.subtype)
131
132    def __hash__(self):
133        return hash(self.media) ^ hash(self.subtype)
134
135class UnknownMagicRuleFormat(ValueError):
136    pass
137
138class DiscardMagicRules(Exception):
139    "Raised when __NOMAGIC__ is found, and caught to discard previous rules."
140    pass
141
142class MagicRule:
143    also = None
144
145    def __init__(self, start, value, mask, word, range):
146        self.start = start
147        self.value = value
148        self.mask = mask
149        self.word = word
150        self.range = range
151
152    rule_ending_re = re.compile(br'(?:~(\d+))?(?:\+(\d+))?\n$')
153
154    @classmethod
155    def from_file(cls, f):
156        """Read a rule from the binary magics file. Returns a 2-tuple of
157        the nesting depth and the MagicRule."""
158        line = f.readline()
159        #print line
160
161        # [indent] '>'
162        nest_depth, line = line.split(b'>', 1)
163        nest_depth = int(nest_depth) if nest_depth else 0
164
165        # start-offset '='
166        start, line = line.split(b'=', 1)
167        start = int(start)
168
169        if line == b'__NOMAGIC__\n':
170            raise DiscardMagicRules
171
172        # value length (2 bytes, big endian)
173        if sys.version_info[0] >= 3:
174            lenvalue = int.from_bytes(line[:2], byteorder='big')
175        else:
176            lenvalue = (ord(line[0])<<8)+ord(line[1])
177        line = line[2:]
178
179        # value
180        #   This can contain newlines, so we may need to read more lines
181        while len(line) <= lenvalue:
182            line += f.readline()
183        value, line = line[:lenvalue], line[lenvalue:]
184
185        # ['&' mask]
186        if line.startswith(b'&'):
187            # This can contain newlines, so we may need to read more lines
188            while len(line) <= lenvalue:
189                line += f.readline()
190            mask, line = line[1:lenvalue+1], line[lenvalue+1:]
191        else:
192            mask = None
193
194        # ['~' word-size] ['+' range-length]
195        ending = cls.rule_ending_re.match(line)
196        if not ending:
197            # Per the spec, this will be caught and ignored, to allow
198            # for future extensions.
199            raise UnknownMagicRuleFormat(repr(line))
200
201        word, range = ending.groups()
202        word = int(word) if (word is not None) else 1
203        range = int(range) if (range is not None) else 1
204
205        return nest_depth, cls(start, value, mask, word, range)
206
207    def maxlen(self):
208        l = self.start + len(self.value) + self.range
209        if self.also:
210            return max(l, self.also.maxlen())
211        return l
212
213    def match(self, buffer):
214        if self.match0(buffer):
215            if self.also:
216                return self.also.match(buffer)
217            return True
218
219    def match0(self, buffer):
220        l=len(buffer)
221        lenvalue = len(self.value)
222        for o in range(self.range):
223            s=self.start+o
224            e=s+lenvalue
225            if l<e:
226                return False
227            if self.mask:
228                test=''
229                for i in range(lenvalue):
230                    if PY3:
231                        c = buffer[s+i] & self.mask[i]
232                    else:
233                        c = ord(buffer[s+i]) & ord(self.mask[i])
234                    test += chr(c)
235            else:
236                test = buffer[s:e]
237
238            if test==self.value:
239                return True
240
241    def __repr__(self):
242        return 'MagicRule(start=%r, value=%r, mask=%r, word=%r, range=%r)' %(
243                                  self.start,
244                                  self.value,
245                                  self.mask,
246                                  self.word,
247                                  self.range)
248
249
250class MagicMatchAny(object):
251    """Match any of a set of magic rules.
252
253    This has a similar interface to MagicRule objects (i.e. its match() and
254    maxlen() methods), to allow for duck typing.
255    """
256    def __init__(self, rules):
257        self.rules = rules
258
259    def match(self, buffer):
260        return any(r.match(buffer) for r in self.rules)
261
262    def maxlen(self):
263        return max(r.maxlen() for r in self.rules)
264
265    @classmethod
266    def from_file(cls, f):
267        """Read a set of rules from the binary magic file."""
268        c=f.read(1)
269        f.seek(-1, 1)
270        depths_rules = []
271        while c and c != b'[':
272            try:
273                depths_rules.append(MagicRule.from_file(f))
274            except UnknownMagicRuleFormat:
275                # Ignored to allow for extensions to the rule format.
276                pass
277            c=f.read(1)
278            if c:
279                f.seek(-1, 1)
280
281        # Build the rule tree
282        tree = []  # (rule, [(subrule,[subsubrule,...]), ...])
283        insert_points = {0:tree}
284        for depth, rule in depths_rules:
285            subrules = []
286            insert_points[depth].append((rule, subrules))
287            insert_points[depth+1] = subrules
288
289        return cls.from_rule_tree(tree)
290
291    @classmethod
292    def from_rule_tree(cls, tree):
293        """From a nested list of (rule, subrules) pairs, build a MagicMatchAny
294        instance, recursing down the tree.
295
296        Where there's only one top-level rule, this is returned directly,
297        to simplify the nested structure. Returns None if no rules were read.
298        """
299        rules = []
300        for rule, subrules in tree:
301            if subrules:
302                rule.also = cls.from_rule_tree(subrules)
303            rules.append(rule)
304
305        if len(rules)==0:
306            return None
307        if len(rules)==1:
308            return rules[0]
309        return cls(rules)
310
311class MagicDB:
312    def __init__(self):
313        self.bytype   = defaultdict(list)  # mimetype -> [(priority, rule), ...]
314
315    def merge_file(self, fname):
316        """Read a magic binary file, and add its rules to this MagicDB."""
317        with open(fname, 'rb') as f:
318            line = f.readline()
319            if line != b'MIME-Magic\0\n':
320                raise IOError('Not a MIME magic file')
321
322            while True:
323                shead = f.readline().decode('ascii')
324                #print(shead)
325                if not shead:
326                    break
327                if shead[0] != '[' or shead[-2:] != ']\n':
328                    raise ValueError('Malformed section heading', shead)
329                pri, tname = shead[1:-2].split(':')
330                #print shead[1:-2]
331                pri = int(pri)
332                mtype = lookup(tname)
333                try:
334                    rule = MagicMatchAny.from_file(f)
335                except DiscardMagicRules:
336                    self.bytype.pop(mtype, None)
337                    rule = MagicMatchAny.from_file(f)
338                if rule is None:
339                    continue
340                #print rule
341
342                self.bytype[mtype].append((pri, rule))
343
344    def finalise(self):
345        """Prepare the MagicDB for matching.
346
347        This should be called after all rules have been merged into it.
348        """
349        maxlen = 0
350        self.alltypes = []  # (priority, mimetype, rule)
351
352        for mtype, rules in self.bytype.items():
353            for pri, rule in rules:
354                self.alltypes.append((pri, mtype, rule))
355                maxlen = max(maxlen, rule.maxlen())
356
357        self.maxlen = maxlen  # Number of bytes to read from files
358        self.alltypes.sort(key=lambda x: x[0], reverse=True)
359
360    def match_data(self, data, max_pri=100, min_pri=0, possible=None):
361        """Do magic sniffing on some bytes.
362
363        max_pri & min_pri can be used to specify the maximum & minimum priority
364        rules to look for. possible can be a list of mimetypes to check, or None
365        (the default) to check all mimetypes until one matches.
366
367        Returns the MIMEtype found, or None if no entries match.
368        """
369        if possible is not None:
370            types = []
371            for mt in possible:
372                for pri, rule in self.bytype[mt]:
373                    types.append((pri, mt, rule))
374            types.sort(key=lambda x: x[0])
375        else:
376            types = self.alltypes
377
378        for priority, mimetype, rule in types:
379            #print priority, max_pri, min_pri
380            if priority > max_pri:
381                continue
382            if priority < min_pri:
383                break
384
385            if rule.match(data):
386                return mimetype
387
388    def match(self, path, max_pri=100, min_pri=0, possible=None):
389        """Read data from the file and do magic sniffing on it.
390
391        max_pri & min_pri can be used to specify the maximum & minimum priority
392        rules to look for. possible can be a list of mimetypes to check, or None
393        (the default) to check all mimetypes until one matches.
394
395        Returns the MIMEtype found, or None if no entries match. Raises IOError
396        if the file can't be opened.
397        """
398        with open(path, 'rb') as f:
399            buf = f.read(self.maxlen)
400        return self.match_data(buf, max_pri, min_pri, possible)
401
402    def __repr__(self):
403        return '<MagicDB (%d types)>' % len(self.alltypes)
404
405class GlobDB(object):
406    def __init__(self):
407        """Prepare the GlobDB. It can't actually be used until .finalise() is
408        called, but merge_file() can be used to add data before that.
409        """
410        # Maps mimetype to {(weight, glob, flags), ...}
411        self.allglobs = defaultdict(set)
412
413    def merge_file(self, path):
414        """Loads name matching information from a globs2 file."""#
415        allglobs = self.allglobs
416        with open(path) as f:
417            for line in f:
418                if line.startswith('#'): continue   # Comment
419
420                fields = line[:-1].split(':')
421                weight, type_name, pattern = fields[:3]
422                weight = int(weight)
423                mtype = lookup(type_name)
424                if len(fields) > 3:
425                    flags = fields[3].split(',')
426                else:
427                    flags = ()
428
429                if pattern == '__NOGLOBS__':
430                    # This signals to discard any previous globs
431                    allglobs.pop(mtype, None)
432                    continue
433
434                allglobs[mtype].add((weight, pattern, tuple(flags)))
435
436    def finalise(self):
437        """Prepare the GlobDB for matching.
438
439        This should be called after all files have been merged into it.
440        """
441        self.exts = defaultdict(list)  # Maps extensions to [(type, weight),...]
442        self.cased_exts = defaultdict(list)
443        self.globs = []                # List of (regex, type, weight) triplets
444        self.literals = {}             # Maps literal names to (type, weight)
445        self.cased_literals = {}
446
447        for mtype, globs in self.allglobs.items():
448          mtype = mtype.canonical()
449          for weight, pattern, flags in globs:
450
451            cased = 'cs' in flags
452
453            if pattern.startswith('*.'):
454                # *.foo -- extension pattern
455                rest = pattern[2:]
456                if not ('*' in rest or '[' in rest or '?' in rest):
457                    if cased:
458                        self.cased_exts[rest].append((mtype, weight))
459                    else:
460                        self.exts[rest.lower()].append((mtype, weight))
461                    continue
462
463            if ('*' in pattern or '[' in pattern or '?' in pattern):
464                # Translate the glob pattern to a regex & compile it
465                re_flags = 0 if cased else re.I
466                pattern = re.compile(fnmatch.translate(pattern), flags=re_flags)
467                self.globs.append((pattern, mtype, weight))
468            else:
469                # No wildcards - literal pattern
470                if cased:
471                    self.cased_literals[pattern] = (mtype, weight)
472                else:
473                    self.literals[pattern.lower()] = (mtype, weight)
474
475        # Sort globs by weight & length
476        self.globs.sort(reverse=True, key=lambda x: (x[2], len(x[0].pattern)) )
477
478    def first_match(self, path):
479        """Return the first match found for a given path, or None if no match
480        is found."""
481        try:
482            return next(self._match_path(path))[0]
483        except StopIteration:
484            return None
485
486    def all_matches(self, path):
487        """Return a list of (MIMEtype, glob weight) pairs for the path."""
488        return list(self._match_path(path))
489
490    def _match_path(self, path):
491        """Yields pairs of (mimetype, glob weight)."""
492        leaf = os.path.basename(path)
493
494        # Literals (no wildcards)
495        if leaf in self.cased_literals:
496            yield self.cased_literals[leaf]
497
498        lleaf = leaf.lower()
499        if lleaf in self.literals:
500            yield self.literals[lleaf]
501
502        # Extensions
503        ext = leaf
504        while 1:
505            p = ext.find('.')
506            if p < 0: break
507            ext = ext[p + 1:]
508            if ext in self.cased_exts:
509                for res in self.cased_exts[ext]:
510                    yield res
511        ext = lleaf
512        while 1:
513            p = ext.find('.')
514            if p < 0: break
515            ext = ext[p+1:]
516            if ext in self.exts:
517                for res in self.exts[ext]:
518                    yield res
519
520        # Other globs
521        for (regex, mime_type, weight) in self.globs:
522            if regex.match(leaf):
523                yield (mime_type, weight)
524
525# Some well-known types
526text = lookup('text', 'plain')
527octet_stream = lookup('application', 'octet-stream')
528inode_block = lookup('inode', 'blockdevice')
529inode_char = lookup('inode', 'chardevice')
530inode_dir = lookup('inode', 'directory')
531inode_fifo = lookup('inode', 'fifo')
532inode_socket = lookup('inode', 'socket')
533inode_symlink = lookup('inode', 'symlink')
534inode_door = lookup('inode', 'door')
535app_exe = lookup('application', 'executable')
536
537_cache_uptodate = False
538
539def _cache_database():
540    global globs, magic, aliases, inheritance, _cache_uptodate
541
542    _cache_uptodate = True
543
544    aliases = {}    # Maps alias Mime types to canonical names
545    inheritance = defaultdict(set) # Maps to sets of parent mime types.
546
547    # Load aliases
548    for path in BaseDirectory.load_data_paths(os.path.join('mime', 'aliases')):
549        with open(path, 'r') as f:
550            for line in f:
551                alias, canonical = line.strip().split(None, 1)
552                aliases[alias] = canonical
553
554    # Load filename patterns (globs)
555    globs = GlobDB()
556    for path in BaseDirectory.load_data_paths(os.path.join('mime', 'globs2')):
557        globs.merge_file(path)
558    globs.finalise()
559
560    # Load magic sniffing data
561    magic = MagicDB()
562    for path in BaseDirectory.load_data_paths(os.path.join('mime', 'magic')):
563        magic.merge_file(path)
564    magic.finalise()
565
566    # Load subclasses
567    for path in BaseDirectory.load_data_paths(os.path.join('mime', 'subclasses')):
568        with open(path, 'r') as f:
569            for line in f:
570                sub, parent = line.strip().split(None, 1)
571                inheritance[sub].add(parent)
572
573def update_cache():
574    if not _cache_uptodate:
575        _cache_database()
576
577def get_type_by_name(path):
578    """Returns type of file by its name, or None if not known"""
579    update_cache()
580    return globs.first_match(path)
581
582def get_type_by_contents(path, max_pri=100, min_pri=0):
583    """Returns type of file by its contents, or None if not known"""
584    update_cache()
585
586    return magic.match(path, max_pri, min_pri)
587
588def get_type_by_data(data, max_pri=100, min_pri=0):
589    """Returns type of the data, which should be bytes."""
590    update_cache()
591
592    return magic.match_data(data, max_pri, min_pri)
593
594def _get_type_by_stat(st_mode):
595    """Match special filesystem objects to Mimetypes."""
596    if stat.S_ISDIR(st_mode): return inode_dir
597    elif stat.S_ISCHR(st_mode): return inode_char
598    elif stat.S_ISBLK(st_mode): return inode_block
599    elif stat.S_ISFIFO(st_mode): return inode_fifo
600    elif stat.S_ISLNK(st_mode): return inode_symlink
601    elif stat.S_ISSOCK(st_mode): return inode_socket
602    return inode_door
603
604def get_type(path, follow=True, name_pri=100):
605    """Returns type of file indicated by path.
606
607    This function is *deprecated* - :func:`get_type2` is more accurate.
608
609    :param path: pathname to check (need not exist)
610    :param follow: when reading file, follow symbolic links
611    :param name_pri: Priority to do name matches. 100=override magic
612
613    This tries to use the contents of the file, and falls back to the name. It
614    can also handle special filesystem objects like directories and sockets.
615    """
616    update_cache()
617
618    try:
619        if follow:
620            st = os.stat(path)
621        else:
622            st = os.lstat(path)
623    except:
624        t = get_type_by_name(path)
625        return t or text
626
627    if stat.S_ISREG(st.st_mode):
628        # Regular file
629        t = get_type_by_contents(path, min_pri=name_pri)
630        if not t: t = get_type_by_name(path)
631        if not t: t = get_type_by_contents(path, max_pri=name_pri)
632        if t is None:
633            if stat.S_IMODE(st.st_mode) & 0o111:
634                return app_exe
635            else:
636                return text
637        return t
638    else:
639        return _get_type_by_stat(st.st_mode)
640
641def get_type2(path, follow=True):
642    """Find the MIMEtype of a file using the XDG recommended checking order.
643
644    This first checks the filename, then uses file contents if the name doesn't
645    give an unambiguous MIMEtype. It can also handle special filesystem objects
646    like directories and sockets.
647
648    :param path: file path to examine (need not exist)
649    :param follow: whether to follow symlinks
650
651    :rtype: :class:`MIMEtype`
652
653    .. versionadded:: 1.0
654    """
655    update_cache()
656
657    try:
658        st = os.stat(path) if follow else os.lstat(path)
659    except OSError:
660        return get_type_by_name(path) or octet_stream
661
662    if not stat.S_ISREG(st.st_mode):
663        # Special filesystem objects
664        return _get_type_by_stat(st.st_mode)
665
666    mtypes = sorted(globs.all_matches(path), key=(lambda x: x[1]), reverse=True)
667    if mtypes:
668        max_weight = mtypes[0][1]
669        i = 1
670        for mt, w in mtypes[1:]:
671            if w < max_weight:
672                break
673            i += 1
674        mtypes = mtypes[:i]
675        if len(mtypes) == 1:
676            return mtypes[0][0]
677
678        possible = [mt for mt,w in mtypes]
679    else:
680        possible = None   # Try all magic matches
681
682    try:
683        t = magic.match(path, possible=possible)
684    except IOError:
685        t = None
686
687    if t:
688        return t
689    elif mtypes:
690        return mtypes[0][0]
691    elif stat.S_IMODE(st.st_mode) & 0o111:
692        return app_exe
693    else:
694        return text if is_text_file(path) else octet_stream
695
696def is_text_file(path):
697    """Guess whether a file contains text or binary data.
698
699    Heuristic: binary if the first 32 bytes include ASCII control characters.
700    This rule may change in future versions.
701
702    .. versionadded:: 1.0
703    """
704    try:
705        f = open(path, 'rb')
706    except IOError:
707        return False
708
709    with f:
710        return _is_text(f.read(32))
711
712if PY3:
713    def _is_text(data):
714        return not any(b <= 0x8 or 0xe <= b < 0x20 or b == 0x7f for b in data)
715else:
716    def _is_text(data):
717        return not any(b <= '\x08' or '\x0e' <= b < '\x20' or b == '\x7f' \
718                            for b in data)
719
720_mime2ext_cache = None
721_mime2ext_cache_uptodate = False
722
723def get_extensions(mimetype):
724    """Retrieve the set of filename extensions matching a given MIMEtype.
725
726    Extensions are returned without a leading dot, e.g. 'py'. If no extensions
727    are registered for the MIMEtype, returns an empty set.
728
729    The extensions are stored in a cache the first time this is called.
730
731    .. versionadded:: 1.0
732    """
733    global _mime2ext_cache, _mime2ext_cache_uptodate
734    update_cache()
735    if not _mime2ext_cache_uptodate:
736        _mime2ext_cache = defaultdict(set)
737        for ext, mtypes in globs.exts.items():
738            for mtype, prio in mtypes:
739                _mime2ext_cache[mtype].add(ext)
740        _mime2ext_cache_uptodate = True
741
742    return _mime2ext_cache[mimetype]
743
744
745def install_mime_info(application, package_file):
746    """Copy 'package_file' as ``~/.local/share/mime/packages/<application>.xml.``
747    If package_file is None, install ``<app_dir>/<application>.xml``.
748    If already installed, does nothing. May overwrite an existing
749    file with the same name (if the contents are different)"""
750    application += '.xml'
751
752    with open(package_file) as f:
753        new_data = f.read()
754
755    # See if the file is already installed
756    package_dir = os.path.join('mime', 'packages')
757    resource = os.path.join(package_dir, application)
758    for x in BaseDirectory.load_data_paths(resource):
759        try:
760            with open(x) as f:
761                old_data = f.read()
762        except:
763            continue
764        if old_data == new_data:
765            return  # Already installed
766
767    global _cache_uptodate
768    _cache_uptodate = False
769
770    # Not already installed; add a new copy
771    # Create the directory structure...
772    new_file = os.path.join(BaseDirectory.save_data_path(package_dir), application)
773
774    # Write the file...
775    with open(new_file, 'w') as f:
776        f.write(new_data)
777
778    # Update the database...
779    command = 'update-mime-database'
780    if os.spawnlp(os.P_WAIT, command, command, BaseDirectory.save_data_path('mime')):
781        os.unlink(new_file)
782        raise Exception("The '%s' command returned an error code!\n" \
783                  "Make sure you have the freedesktop.org shared MIME package:\n" \
784                  "http://standards.freedesktop.org/shared-mime-info/" % command)
785