#!/usr/bin/env python

from __future__ import with_statement

import subprocess
import inspect
import os
import sys
import stat
import errno
import string
import shutil
import tempfile
import filecmp
import threading
import psutil

import fuse

#from fuse import Fuse, FuseOSError

def _max(iterable):
    """
    Max is zero, even if iterable is empty

    >>> _max([])
    0
    >>> _max([5])
    5
    >>> _max([1, 2])
    2
    """
    try:
        return max(iterable)
    except ValueError:
        return 0


class Filesystem(fuse.Operations):
    def __init__(self, root):
        self.root = root
        self._rwlock = threading.Lock()
        self._openfds = dict()
        self._openfiles = dict()

    def _runcommand(self, path, output):
        cmd_template = string.Template('g++ -P -E -xc++ -o - $input > $output')

        fullpath = self._getrealpath(path)

        cmd = cmd_template.substitute(output=output, input=fullpath)
        subprocess.call(cmd, shell=True)

        original_size = self.getattr(path)['st_size']
        current_size = os.lstat(output).st_size

        extrachars = ''
        for i in range(original_size - current_size):
            extrachars += '\n'

        with open(output, 'a') as o:
            o.write(extrachars)


    def _getnewfd(self, path):
        with self._rwlock:
            nextavailable = _max(self._openfds.keys()) +1
            self._openfds[nextavailable] = tempfile.mkstemp()
            name = self._openfds[nextavailable][1]
            self._openfiles[path] = nextavailable
        self._runcommand(path, name)
        return nextavailable

    def _getrealpath(self, path):
        """
        Get true path for proxy requests

        >>> fs=Filesystem('/foo')
        >>> fs._getrealpath('/bar')
        '/foo/bar'
        >>> fs._getrealpath('/')
        '/foo/'
        """
        return os.path.join(self.root, path[1:])

    def getattr(self, path, fh=None):
        st = os.lstat(self._getrealpath(path))
        answer = dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime',
            'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid'))

        return answer

    def statfs(self, path):
        stv = os.statvfs(self._getrealpath(path))
        answer =  dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',
            'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',
            'f_frsize', 'f_namemax'))
        return answer

    def readdir(self, path, fh):
        return ['.', '..'] + os.listdir(self._getrealpath(path))

    def open(self, path, fi):
        # Run command and return fd to it
        fd = self._getnewfd(path)

        fi.fh = fd
        fi.direct_io = True
        # No OS caching allowed, all reads must go through us

    def release(self, path, fi):
        fh = fi.fh
        with self._rwlock:
            assert(self._openfiles[path] == fh)
            os.remove(self._openfds[fh][1])
            del self._openfds[fh]
            del self._openfiles[path]

    def read(self, path, size, offset, fi):
        pid = fuse.fuse_get_context()[2]
        invokingProcess = psutil.Process(pid)
        print "Reading for", invokingProcess.cmdline
        fh = fi.fh
        assert(self._openfiles[path] == fh)
        fd = os.open(self._openfds[fh][1], os.O_RDONLY)
        os.lseek(fd, offset, os.SEEK_SET)
        buf = os.read(fd, size)
        os.close(fd)
        return buf

    # unused features
    access = None
    flush = None
    getxattr = None
    listxattr = None
    opendir = None
    releasedir = None

def main(mountpoint, root):
    fuse.FUSE(Filesystem(root), mountpoint, raw_fi=True, foreground=True)

if __name__ == '__main__':
    import doctest
    doctest.testmod()
    main(sys.argv[2], sys.argv[1])

@@ -704,16 +704,138 @@
 dict()%0A%0A
+    def getextraargs(self, pid):%0A        process = psutil.Process(pid)%0A        args = process.cmdline%0A        return ''%0A%0A%0A
     def 
@@ -864,16 +864,30 @@
 , output
+, extraargs=''
 ):%0A     
@@ -941,12 +941,28 @@
 c++ 
--o -
+$extraargs -o -  - %3C
  $in
@@ -1089,16 +1089,37 @@
 fullpath
+, extraargs=extraargs
 )%0A      
@@ -1458,32 +1458,37 @@
 newfd(self, path
+, pid
 ):%0A        with 
@@ -1721,16 +1721,60 @@
 vailable
+%0A%0A        extraargs = self.getextraargs(pid)
 %0A       
@@ -1801,16 +1801,27 @@
 th, name
+, extraargs
 )%0A      
@@ -2874,16 +2874,57 @@
 d to it%0A
+        pid = fuse.fuse_get_context()%5B2%5D%0A
         
@@ -2943,24 +2943,29 @@
 etnewfd(path
+, pid
 )%0A%0A        f
@@ -3357,148 +3357,8 @@
 i):%0A
-        pid = fuse.fuse_get_context()%5B2%5D%0A        invokingProcess = psutil.Process(pid)%0A        print %22Reading for%22, invokingProcess.cmdline%0A
     

Add getextraargs function stub