#!/usr/bin/env python
# XML-FS, sous licence GPL, LGPL ou BSD au choix
# Romain Guy <romain.guy@jext.org>

################################################
# IMPORTS
################################################

import array
import errno
import os
import stat
import time

from xml.dom import *
from xml.dom.minidom import *
from fuse import Fuse

################################################
# INODE
################################################

class XMLInode:
    def __init__(self):
        self.ino = 0
        self.mode = 0
        self.dev = 0
        self.nlink = 0
        self.uid = 0
        self.gid = 0
        self.size = 0
        self.atime = 0
        self.mtime = 0
        self.ctime = 0

################################################
# SYSTEME DE FICHIERS
################################################

class XMLFs(Fuse):
    def __init__(self, *args, **kw):
        Fuse.__init__(self, *args, **kw)
        self.mounted = self.optdict['xml']
        self.doc     = parse(self.mounted)
        self._restat()

    def _restat(self):
        self.stats   = os.lstat(self.mounted)
        self.atime   = os.path.getatime(self.mounted)
        self.mtime   = os.path.getmtime(self.mounted)
        self.ctime   = os.path.getctime(self.mounted)

    def _pathToNode(self, path):
        node = self.doc.documentElement
        for element in path.split('/'):
            if len(element) == 0: continue
            parts = element.split('-')
            if len(parts) < 2: return None
            name = "-".join(parts[0:-1])
            index = int(parts[-1])
            if name != "#text":
                if name[0] == '.':
                    node = node.getAttributeNode(name[1:])
                else:
                    nodes = node.getElementsByTagName(name)
                    if len(nodes) == 0: return None
                    node = nodes[index - 1]
            else:
                i = 1
                found = 0
                for child in node.childNodes:
                    if child.nodeType == Node.TEXT_NODE:
                        if index == i:
                            node = child
                            found = 1
                            break
                        else:
                            i += 1
                if not found: return None
            if node.nodeType != Node.ELEMENT_NODE: break
        return node

    def getattr(self, path):
        node = self._pathToNode(path)
        inode = XMLInode()
        if node is None:
            e = OSError("No such file " + path)
            e.errno = errno.ENOENT
            raise e

        inode.mode  = self.stats.st_mode
        inode.mode ^= stat.S_IFREG
        inode.gid   = self.stats.st_gid
        inode.uid   = self.stats.st_uid
        inode.atime = self.atime
        inode.mtime = self.mtime
        inode.ctime = self.ctime

        if node.nodeType == Node.ELEMENT_NODE:
            inode.mode |= stat.S_IFDIR
            inode.nlink = 2
        else:
            inode.mode |= stat.S_IFREG
            inode.nlink = 1
            inode.size = len(node.nodeValue)

        return (inode.mode, inode.ino, inode.dev, inode.nlink, inode.uid, inode.gid, inode.size, inode.atime, inode.mtime, inode.ctime)

    def readlink(self, path):
        return path

    def getdir(self, path):
        dirs = [('.', 0), ('..', 0)]
        counters = { }
        baseNode = self._pathToNode(path)
        for i in range(baseNode.attributes.length):
            dirs.append(('.' + baseNode.attributes.item(i).name.encode('latin1') + '-1', 0))
        for node in baseNode.childNodes:
            name = str(node.nodeName)
            if name not in counters: counters[name] = 1
            else: counters[name] += 1
            dirs.append((name + '-' + str(counters[name]), 0))
        return dirs

    def unlink(self, path):
        return 0

    def rmdir(self, path):
        node = self._pathToNode(path)
        node.parentNode.removeChild(node)
        return 0

    def symlink(self, path, path1):
        return 0

    def rename(self, path, path1):
        return 0

    def link(self, path, path1):
        return 0

    def chmod(self, path, mode):
        v = os.chmod(self.mounted, mode)
        self._restat()
        return v

    def chown(self, path, user, group):
        v = os.chmown(self.mounted, mode)
        self._restat()
        return v

    def truncate(self, path, size):
        return 0

    def mknod(self, path, mode, dev):
        return -errno.EINVAL

    def mkdir(self, path, mode):
        return 0

    def utime(self, path, times):
        return self.utime

    def open(self, path, flags):
        return 0
    
    def read(self, path, len, offset):
        node = self._pathToNode(path)
        if node is None:
            e = OSError("Error reading file " + path)
            e.errno = errno.EINVAL
            raise e
        if node.nodeType != Node.ELEMENT_NODE:
            return node.nodeValue[offset:offset + len].encode('latin1')
        return ""
    
    def write(self, path, buf, off):
        e = OSError("Error writing file " + path)
        e.errno = errno.EINVAL
        raise e
    
    def release(self, path, flags):
        return 0

    def statfs(self):
        blocks_size = 1024
        blocks = 1000 * 1024
        blocks_free = blocks
        files = 0
        files_free = 0
        namelen = 256
        return (blocks_size, blocks, blocks_free, files, files_free, namelen)

    def fsync(self, path, isfsyncfile):
        return 0
    
if __name__ == '__main__':
    fs = XMLFs()
    fs.flags = 0
    fs.multithreaded = 0
    fs.main()