2 """XML Virtual FileSystem for Midnight Commander
4 The script requires Midnight Commander 3.1+
5 (http://www.midnight-commander.org/), Python 2.4+ (http://www.python.org/).
7 For mc 4.7+ put the script in $HOME/.mc/extfs.d.
8 For older versions put it in /usr/[local/][lib|share]/mc/extfs
9 and add a line "xml" to the /usr/[local/][lib|share]/mc/extfs/extfs.ini.
10 Make the script executable.
12 For mc 4.7+ run this "cd" command in the Midnight Commander (in the "bindings"
13 file the command is "%cd"): cd file/xml://; In older versions it is
14 cd file#xml, where "file" is the name of your XML file.
16 The VFS represents tags as directories; the directories are numbered to
17 distinguish tags with the same name; also numbering helps to sort tags by their
18 order in XML instead of sorting them by name. Attributes, text nodes and
19 comments are represented as text files; attributes are shown in a file named
20 "attributes", attributes are listed in the file as name=value lines (I
21 deliberately ignore a small chance of newline characters in values); names and
22 values are reencoded to the console encoding. Text nodes and comments are
23 collected in a file named "text", stripped and reencoded. The filesystem is
26 The VFS was inspired by a FUSE xmlfs: https://github.com/halhen/xmlfs
31 __author__ = "Oleg Broytman <phd@phdru.name>"
32 __copyright__ = "Copyright (C) 2013 PhiloSoft Design"
37 import xml.dom.minidom
46 # Get the default charset.
48 lcAll = locale.getdefaultlocale()
49 except locale.Error, err:
50 print >>sys.stderr, "WARNING:", err
54 default_encoding = lcAll[1]
57 default_encoding = locale.getpreferredencoding()
58 except locale.Error, err:
59 print >>sys.stderr, "WARNING:", err
60 default_encoding = sys.getdefaultencoding()
62 default_encoding = sys.getdefaultencoding()
65 logger = logging.getLogger('xml-mcextfs')
66 log_err_handler = logging.StreamHandler(sys.stderr)
67 logger.addHandler(log_err_handler)
68 logger.setLevel(logging.INFO)
72 XML Virtual FileSystem for Midnight Commander version %s
76 This is not a program. Put the script in $HOME/.mc/extfs.d or
77 /usr/[local/][lib|share]/mc/extfs. For more information read the source!""",
78 __version__, __author__, __copyright__
83 locale.setlocale(locale.LC_ALL, '')
85 def _attrs2text(attrs):
86 attrs = [attrs.item(i) for i in range (attrs.length)]
87 return '\n'.join(["%s=%s" %
88 (a.name.encode(default_encoding, "replace"),
89 a.value.encode(default_encoding, "replace"))
92 def _collect_text(node):
94 for element in node.childNodes:
97 elif element.nodeType == element.COMMENT_NODE:
98 text = u"<!--%s-->" % element.nodeValue
99 elif element.nodeType == element.TEXT_NODE:
100 text = element.nodeValue.strip()
102 xml_error("Unknown node type %d" % element.nodeType)
103 if text: text_accumulator.append(text)
104 return '\n'.join(text_accumulator).encode(default_encoding, "replace")
106 def _list(node, path=''):
107 childNodes = node.childNodes
109 for element in childNodes:
110 if element.localName:
113 width = int(math.log10(n))+1
114 template = "%%0%dd" % width
118 for element in childNodes:
119 if element.localName:
122 subpath = '%s/%s %s' % (path, template % n, element.localName)
124 subpath = '%s %s' % (template % n, element.localName)
125 subpath_encoded = subpath.encode(default_encoding, "replace")
126 print "dr--r--r-- 1 user group 0 Jan 1 00:00 %s" % subpath_encoded
127 attrs = element.attributes
129 attr_text = _attrs2text(attrs)
130 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s/attributes" % (
131 len(attr_text), subpath_encoded)
132 _list(element, subpath)
134 text = _collect_text(node)
136 print "-r--r--r-- 1 user group %d Jan 1 00:00 %s/text" % (
137 len(text), path.encode(default_encoding, "replace"))
140 """List the entire VFS"""
142 dom = xml.dom.minidom.parse(sys.argv[2])
146 def _get_child_node(node, i):
148 for element in node.childNodes:
149 if element.localName:
153 xml_error('There are less than %d nodes' % i)
156 """Extract a file from the VFS"""
158 node = xml.dom.minidom.parse(sys.argv[2])
159 xml_filename = sys.argv[3]
160 real_filename = sys.argv[4]
162 for path_comp in xml_filename.split('/'):
164 i = int(path_comp.split(' ', 1)[0])
165 node = _get_child_node(node, i)
166 elif path_comp in ('attributes', 'text'):
169 xml_error('Unknown file')
171 if path_comp == 'attributes':
172 attrs = node.attributes
174 text = _attrs2text(attrs)
176 xml_error('There are no attributes')
178 if path_comp == 'text':
179 text = _collect_text(node)
181 outfile = open(real_filename, 'w')
187 """Put a file to the VFS"""
188 sys.exit("XML VFS doesn't support adding files (read-only filesystem)")
191 """Remove a file from the VFS"""
192 sys.exit("XML VFS doesn't support removing files/directories (read-only filesystem)")
194 mcxml_rmdir = mcxml_rm
197 """Create a directory in the VFS"""
198 sys.exit("XML VFS doesn't support creating directories (read-only filesystem)")
201 def xml_error(error_str):
202 logger.critical("Error walking XML file: %s", error_str)
205 command = sys.argv[1]
206 procname = "mcxml_" + command
209 if not g.has_key(procname):
210 logger.critical("Unknown command %s", command)
218 logger.exception("Error during run")