4 from xml
.parsers
import expat
5 from xml
.dom
import XML_NAMESPACE
9 home_mime
= os
.path
.join(os
.environ
['HOME'], '.mime')
10 user_override
= os
.path
.join(home_mime
, 'packages', 'Override.xml')
12 FREE_NS
='http://www.freedesktop.org/standards/shared-mime-info'
14 class Invalid(Exception):
19 types
[name
] = MIME_Type(name
)
23 def __init__(self
, name
):
24 assert name
not in types
25 self
.media
, self
.subtype
= name
.split('/')
33 def add_from_node(self
, file):
34 self
.sources
.append(Source(file))
36 def add_comment(self
, lang
, comment
):
37 self
.comments
.append((lang
, comment
))
39 def add_xml(self
, uri
, name
):
40 self
.xml
.append((uri
, name
))
42 def add_magic(self
, prio
, root
):
43 self
.magic
.append((prio
, root
))
45 def add_other(self
, element
):
46 self
.others
.append(element
)
48 def add_glob(self
, pattern
):
49 self
.globs
.append(pattern
)
51 def get_comment(self
):
53 for lang
, comment
in self
.comments
:
57 return best
or self
.get_name()
60 return self
.media
+ '/' + self
.subtype
62 def get_comments(self
): return map(Comment
, self
.comments
)
63 def get_globs(self
): return map(Glob
, self
.globs
)
64 def get_magic(self
): return map(Magic
, self
.magic
)
65 def get_xml(self
): return map(XML
, self
.xml
)
66 def get_others(self
): return map(Other
, self
.others
)
69 "MIME_Type.get_* functions return a list of these."
70 def __init__(self
, item
):
74 return "<%s>" % self
.item
76 def add_subtree(self
, model
, iter):
80 return cmp(str(self
), str(b
))
86 if not hasattr(self
, 'add_edit_widgets'):
87 rox
.alert('Sorry, MIME-Editor does not support editing fields of this type')
91 box
.set_has_separator(False)
92 vbox
= g
.VBox(False, 4)
93 vbox
.set_border_width(4)
94 box
.vbox
.pack_start(vbox
, True, True, 0)
95 self
.add_edit_widgets(vbox
)
96 box
.add_button(g
.STOCK_CANCEL
, g
.RESPONSE_CANCEL
)
97 box
.add_button(g
.STOCK_OK
, g
.RESPONSE_OK
)
98 box
.set_default_response(g
.RESPONSE_OK
)
102 if resp
== g
.RESPONSE_OK
:
104 self
.commit_edit(box
)
111 def commit_edit(self
, box
):
112 raise Invalid('TODO: Not implemented')
114 class Comment(Field
):
116 lang
, data
= self
.item
118 lang
= '[' + lang
+ '] '
125 return "Match '%s'" % self
.item
127 def add_edit_widgets(self
, vbox
):
129 g
.Label("Enter a glob pattern which matches files of this type.\n"
130 "Special characters are:\n"
131 "? - any one character\n"
132 "* - zero or more characters\n"
133 "[abc] - any character between the brackets\n"
134 "Example: '*.html' matches all files ending in '.html'"),
136 self
.entry
= g
.Entry()
137 self
.entry
.set_text(self
.item
)
138 vbox
.pack_start(self
.entry
, False, True, 0)
139 self
.entry
.set_activates_default(True)
141 def commit_edit(self
, box
):
142 new
= self
.entry
.get_text()
144 raise Invalid("Pattern can't be empty")
145 raise Invalid('TODO')
149 prio
, match
= self
.item
150 return "Match with priority %s" % prio
152 def add_subtree(self
, model
, parent
):
153 def build(match
, parent
):
154 for m
in match
.matches
:
155 text
= '%s at %s = %s' % (m
.type, m
.offset
, m
.value
)
157 text
+= ' masked with ' + m
.mask
158 iter = model
.append(parent
)
159 model
.set(iter, 0, text
, 1, m
)
161 build(self
.item
[1], parent
)
163 def __cmp__(self
, b
):
164 ret
= cmp(str(self
), str(b
))
166 return cmp(self
.item
[1], b
.item
[1])
170 return "<%s> with namespace '%s'" % (self
.item
[1], self
.item
[0])
172 class Other(Field
): pass
175 def __init__(self
, type, attrs
):
178 def start(self
, element
, attrs
): pass
179 def data(self
, data
): pass
182 class CommentParser(FieldParser
):
183 def __init__(self
, type, attrs
):
184 FieldParser
.__init
__(self
, type, attrs
)
185 self
.lang
= attrs
.get(XML_NAMESPACE
+ ' lang', None)
188 def data(self
, data
):
192 self
.type.add_comment(self
.lang
, self
.comment
)
195 def __init__(self
, parent
):
199 def __cmp__(self
, b
):
201 for x
, y
in zip(self
.matches
, b
.matches
):
209 return cmp(self
.type, b
.type) or cmp(self
.offset
, b
.offset
) or \
210 cmp(self
.value
, b
.value
) or cmp(self
.mask
, b
.mask
) or \
214 rox
.alert("Editing of matches isn't currently supported")
216 class MagicParser(FieldParser
):
217 def __init__(self
, type, attrs
):
218 FieldParser
.__init
__(self
, type, attrs
)
219 self
.prio
= attrs
.get('priority', 50)
220 self
.match
= Match(None)
222 def start(self
, element
, attrs
):
223 new
= Match(self
.match
)
224 new
.offset
= attrs
.get('offset', '?')
225 new
.type = attrs
.get('type', '?')
226 new
.value
= attrs
.get('value', '?')
227 new
.mask
= attrs
.get('mask', None)
228 self
.match
.matches
.append(new
)
231 if self
.match
.parent
:
232 self
.match
= self
.match
.parent
234 self
.type.add_magic(self
.prio
, self
.match
)
242 def parse(self
, path
):
243 parser
= expat
.ParserCreate(namespace_separator
= ' ')
244 parser
.StartElementHandler
= self
.start
245 parser
.EndElementHandler
= self
.end
246 parser
.CharacterDataHandler
= self
.data
247 parser
.ParseFile(file(path
))
249 def start(self
, element
, attrs
):
252 assert element
== FREE_NS
+ ' mime-info'
253 elif self
.level
== 2:
254 assert element
== FREE_NS
+ ' mime-type'
255 self
.type = get_type(attrs
['type'])
256 elif self
.level
== 3:
257 if element
== FREE_NS
+ ' comment':
258 self
.handler
= CommentParser(self
.type, attrs
)
259 elif element
== FREE_NS
+ ' glob':
260 self
.type.add_glob(attrs
['pattern'])
261 elif element
== FREE_NS
+ ' magic':
262 self
.handler
= MagicParser(self
.type, attrs
)
263 elif element
== FREE_NS
+ ' root-XML':
264 self
.type.add_xml(attrs
['namespaceURI'], attrs
['localName'])
266 self
.type.add_other(element
)
269 self
.handler
.start(element
, attrs
)
271 def end(self
, element
):
277 elif self
.level
== 2:
280 def data(self
, data
):
282 self
.handler
.data(data
)
285 if not path
.endswith('.xml'): return
286 if not os
.path
.exists(path
):
292 rox
.report_exception()
295 from xml
.dom
import minidom
296 if os
.path
.exists(user_override
):
297 doc
= minidom
.parse(user_override
)
299 doc
= minidom
.Document()
300 node
= doc
.createElementNS(FREE_NS
, 'mime-info')
301 doc
.appendChild(node
)
302 node
.setAttributeNS(XMLNS_NAMESPACE
, 'xmlns', FREE_NS
)
305 def write_override(doc
):
306 home_packages
= os
.path
.join(home_mime
, 'packages')
307 if not os
.path
.isdir(home_packages
):
308 os
.makedirs(home_packages
)
309 path
= os
.path
.join(home_packages
, 'Override.xml.new')
310 doc
.writexml(file(path
, 'w'))
311 os
.rename(path
, path
[:-4])
320 os
.execlp('update-mime-database', 'update-mime-database', home_mime
)
324 rox
.info(os
.fdopen(r
, 'r').read())
328 __main__
.box
.update()
333 root
= doc
.documentElement
334 type_node
= doc
.createElementNS(FREE_NS
, 'mime-type')
335 root
.appendChild(type_node
)
337 type_node
.setAttributeNS(None, 'type', name
)
341 __main__
.box
.show_type(name
)
343 def delete_type(name
):
344 from xml
.dom
import Node
347 for c
in doc
.documentElement
.childNodes
:
348 if c
.nodeType
!= Node
.ELEMENT_NODE
: continue
349 if c
.nodeName
!= 'mime-type': continue
350 if c
.namespaceURI
!= FREE_NS
: continue
351 if c
.getAttributeNS(None, 'type') != name
: continue
352 doc
.documentElement
.removeChild(c
)
355 rox
.alert("No user-provided information about this type -- can't remove anything")
357 if not rox
.confirm("Really remove all user-specified information about type %s?" % name
,
363 "(Re)read the database."
366 for mime_dir
in ['/usr/share/mime', '/usr/local/share/mime', home_mime
]:
367 packages_dir
= os
.path
.join(mime_dir
, 'packages')
368 if not os
.path
.isdir(packages_dir
):
370 packages
= os
.listdir(packages_dir
)
372 for package
in packages
:
373 if package
== 'Override.xml' and mime_dir
is home_mime
: continue
374 scan_file(os
.path
.join(packages_dir
, package
))
375 scan_file(user_override
)