]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
macfuse: move macfuse files around to simplify pythonpath
authorrobk-tahoe <robk-tahoe@allmydata.com>
Wed, 20 Feb 2008 00:18:17 +0000 (17:18 -0700)
committerrobk-tahoe <robk-tahoe@allmydata.com>
Wed, 20 Feb 2008 00:18:17 +0000 (17:18 -0700)
the mac/macfuse subdirectory needed to be added to the pythonpath in order
to build a binary incorporating the mac fuse system.  this change should
make those modules accessible relative to the mac/ directory which is
implicitly included in the .app build process.

13 files changed:
mac/allmydata_tahoe.py
mac/fuse.py [new file with mode: 0644]
mac/fuseparts/__init__.py [new file with mode: 0644]
mac/fuseparts/_fusemodule.so [new file with mode: 0644]
mac/fuseparts/setcompatwrap.py [new file with mode: 0644]
mac/fuseparts/subbedopts.py [new file with mode: 0644]
mac/macfuse/fuse.py [deleted file]
mac/macfuse/fuseparts/__init__.py [deleted file]
mac/macfuse/fuseparts/_fusemodule.so [deleted file]
mac/macfuse/fuseparts/setcompatwrap.py [deleted file]
mac/macfuse/fuseparts/subbedopts.py [deleted file]
mac/macfuse/tahoefuse.py [deleted file]
mac/tahoefuse.py [new file with mode: 0644]

index cd4c780ea672de26a7da9aec1810c54b8009d1b5..a21fc796813fac4ead858ecb911e481552d52619 100644 (file)
@@ -27,8 +27,8 @@ class FuseOptions(usage.Options):
         self.args = args
 
 def fuse(config, stdout, stderr):
-    import macfuse.tahoefuse
-    macfuse.tahoefuse.main(config.args)
+    import tahoefuse
+    tahoefuse.main(config.args)
 
 class FuseRunnerExtension(object):
     subCommands = [
diff --git a/mac/fuse.py b/mac/fuse.py
new file mode 100644 (file)
index 0000000..51f602b
--- /dev/null
@@ -0,0 +1,997 @@
+#
+#    Copyright (C) 2001  Jeff Epler  <jepler@unpythonic.dhs.org>
+#    Copyright (C) 2006  Csaba Henk  <csaba.henk@creo.hu>
+#
+#    This program can be distributed under the terms of the GNU LGPL.
+#    See the file COPYING.
+#
+
+
+# suppress version mismatch warnings
+try:
+    import warnings
+    warnings.filterwarnings('ignore',
+                            'Python C API version mismatch',
+                            RuntimeWarning,
+                            )
+except:
+    pass
+
+from string import join
+import sys
+from errno import *
+from os import environ
+import re
+from fuseparts import __version__
+from fuseparts._fuse import main, FuseGetContext, FuseInvalidate
+from fuseparts._fuse import FuseError, FuseAPIVersion
+from fuseparts.subbedopts import SubOptsHive, SubbedOptFormatter
+from fuseparts.subbedopts import SubbedOptIndentedFormatter, SubbedOptParse
+from fuseparts.subbedopts import SUPPRESS_HELP, OptParseError
+from fuseparts.setcompatwrap import set
+
+
+##########
+###
+###  API specification API.
+###
+##########
+
+# The actual API version of this module
+FUSE_PYTHON_API_VERSION = (0, 2)
+
+def __getenv__(var, pattern = '.', trans = lambda x: x):
+    """
+    Fetch enviroment variable and optionally transform it. Return `None` if
+    variable is unset. Bail out if value of variable doesn't match (optional)
+    regex pattern.
+    """
+
+    if var not in environ:
+        return None
+    val = environ[var]
+    rpat = pattern
+    if not isinstance(rpat, type(re.compile(''))):
+        rpat = re.compile(rpat)
+    if not rpat.search(val):
+        raise RuntimeError("env var %s doesn't match required pattern %s" % \
+                           (var, `pattern`))
+    return trans(val)
+
+def get_fuse_python_api():
+    if fuse_python_api:
+        return fuse_python_api
+    elif compat_0_1:
+        # deprecated way of API specification
+        return (0,1)
+
+def get_compat_0_1():
+    return get_fuse_python_api() == (0, 1)
+
+# API version to be used
+fuse_python_api = __getenv__('FUSE_PYTHON_API', '^[\d.]+$',
+                              lambda x: tuple([int(i) for i in x.split('.')]))
+
+# deprecated way of API specification
+compat_0_1 = __getenv__('FUSE_PYTHON_COMPAT', '^(0.1|ALL)$', lambda x: True)
+
+fuse_python_api = get_fuse_python_api()
+
+##########
+###
+###  Parsing for FUSE.
+###
+##########
+
+
+
+class FuseArgs(SubOptsHive):
+    """
+    Class representing a FUSE command line.
+    """
+
+    fuse_modifiers = {'showhelp': '-ho',
+                      'showversion': '-V',
+                      'foreground': '-f'}
+
+    def __init__(self):
+
+        SubOptsHive.__init__(self)
+
+        self.modifiers = {}
+        self.mountpoint = None
+
+        for m in self.fuse_modifiers:
+            self.modifiers[m] = False
+
+    def __str__(self):
+        return '\n'.join(['< on ' + str(self.mountpoint) + ':',
+                          '  ' + str(self.modifiers), '  -o ']) + \
+               ',\n     '.join(self._str_core()) + \
+               ' >'
+
+    def getmod(self, mod):
+        return self.modifiers[mod]
+
+    def setmod(self, mod):
+        self.modifiers[mod] = True
+
+    def unsetmod(self, mod):
+        self.modifiers[mod] = False
+
+    def mount_expected(self):
+        if self.getmod('showhelp'):
+            return False
+        if self.getmod('showversion'):
+            return False
+        return True
+
+    def assemble(self):
+        """Mangle self into an argument array"""
+
+        self.canonify()
+        args = [sys.argv and sys.argv[0] or "python"]
+        if self.mountpoint:
+            args.append(self.mountpoint)
+        for m, v in self.modifiers.iteritems():
+            if v:
+                args.append(self.fuse_modifiers[m])
+
+        opta = []
+        for o, v in self.optdict.iteritems():
+                opta.append(o + '=' + v)
+        opta.extend(self.optlist)
+
+        if opta:
+            args.append("-o" + ",".join(opta))
+
+        return args
+
+    def filter(self, other=None):
+        """
+        Same as for SubOptsHive, with the following difference:
+        if other is not specified, `Fuse.fuseoptref()` is run and its result
+        will be used.
+        """
+
+        if not other:
+            other = Fuse.fuseoptref()
+
+        return SubOptsHive.filter(self, other)
+
+
+
+class FuseFormatter(SubbedOptIndentedFormatter):
+
+    def __init__(self, **kw):
+        if not 'indent_increment' in kw:
+            kw['indent_increment'] = 4
+        SubbedOptIndentedFormatter.__init__(self, **kw)
+
+    def store_option_strings(self, parser):
+        SubbedOptIndentedFormatter.store_option_strings(self, parser)
+        # 27 is how the lib stock help appears
+        self.help_position = max(self.help_position, 27)
+        self.help_width = self.width - self.help_position
+
+
+class FuseOptParse(SubbedOptParse):
+    """
+    This class alters / enhances `SubbedOptParse` so that it's
+    suitable for usage with FUSE.
+
+    - When adding options, you can use the `mountopt` pseudo-attribute which
+      is equivalent with adding a subopt for option ``-o``
+      (it doesn't require an option argument).
+
+    - FUSE compatible help and version printing.
+
+    - Error and exit callbacks are relaxed. In case of FUSE, the command
+      line is to be treated as a DSL [#]_. You don't wanna this module to
+      force an exit on you just because you hit a DSL syntax error.
+
+    - Built-in support for conventional FUSE options (``-d``, ``-f`, ``-s``).
+      The way of this can be tuned by keyword arguments, see below.
+
+    .. [#] http://en.wikipedia.org/wiki/Domain-specific_programming_language
+
+    Keyword arguments for initialization
+    ------------------------------------
+
+    standard_mods
+      Boolean [default is `True`].
+      Enables support for the usual interpretation of the ``-d``, ``-f``
+      options.
+
+    fetch_mp
+      Boolean [default is `True`].
+      If it's True, then the last (non-option) argument
+      (if there is such a thing) will be used as the FUSE mountpoint.
+
+    dash_s_do
+      String: ``whine``, ``undef``, or ``setsingle`` [default is ``whine``].
+      The ``-s`` option -- traditionally for asking for single-threadedness --
+      is an oddball: single/multi threadedness of a fuse-py fs doesn't depend
+      on the FUSE command line, we have direct control over it.
+
+      Therefore we have two conflicting principles:
+
+      - *Orthogonality*: option parsing shouldn't affect the backing `Fuse`
+        instance directly, only via its `fuse_args` attribute.
+
+      - *POLS*: behave like other FUSE based fs-es do. The stock FUSE help
+        makes mention of ``-s`` as a single-threadedness setter.
+
+      So, if we follow POLS and implement a conventional ``-s`` option, then
+      we have to go beyond the `fuse_args` attribute and set the respective
+      Fuse attribute directly, hence violating orthogonality.
+
+      We let the fs authors make their choice: ``dash_s_do=undef`` leaves this
+      option unhandled, and the fs author can add a handler as she desires.
+      ``dash_s_do=setsingle`` enables the traditional behaviour.
+
+      Using ``dash_s_do=setsingle`` is not problematic at all, but we want fs
+      authors be aware of the particularity of ``-s``, therefore the default is
+      the ``dash_s_do=whine`` setting which raises an exception for ``-s`` and
+      suggests the user to read this documentation.
+
+    dash_o_handler
+      Argument should be a SubbedOpt instance (created with
+      ``action="store_hive"`` if you want it to be useful).
+      This lets you customize the handler of the ``-o`` option. For example,
+      you can alter or suppress the generic ``-o`` entry in help output.
+    """
+
+    def __init__(self, *args, **kw):
+
+        self.mountopts = []
+
+        self.fuse_args = \
+            'fuse_args' in kw and kw.pop('fuse_args') or FuseArgs()
+        dsd = 'dash_s_do' in kw and kw.pop('dash_s_do') or 'whine'
+        if 'fetch_mp' in kw:
+            self.fetch_mp = bool(kw.pop('fetch_mp'))
+        else:
+            self.fetch_mp = True
+        if 'standard_mods' in kw:
+            smods = bool(kw.pop('standard_mods'))
+        else:
+            smods = True
+        if 'fuse' in kw:
+            self.fuse = kw.pop('fuse')
+        if not 'formatter' in kw:
+            kw['formatter'] = FuseFormatter()
+        doh = 'dash_o_handler' in kw and kw.pop('dash_o_handler')
+
+        SubbedOptParse.__init__(self, *args, **kw)
+
+        if doh:
+            self.add_option(doh)
+        else:
+            self.add_option('-o', action='store_hive',
+                            subopts_hive=self.fuse_args, help="mount options",
+                            metavar="opt,[opt...]")
+
+        if smods:
+            self.add_option('-f', action='callback',
+                            callback=lambda *a: self.fuse_args.setmod('foreground'),
+                            help=SUPPRESS_HELP)
+            self.add_option('-d', action='callback',
+                            callback=lambda *a: self.fuse_args.add('debug'),
+                            help=SUPPRESS_HELP)
+
+        if dsd == 'whine':
+            def dsdcb(option, opt_str, value, parser):
+                raise RuntimeError, """
+
+! If you want the "-s" option to work, pass
+!
+!   dash_s_do='setsingle'
+!
+! to the Fuse constructor. See docstring of the FuseOptParse class for an
+! explanation why is it not set by default.
+"""
+
+        elif dsd == 'setsingle':
+            def dsdcb(option, opt_str, value, parser):
+                self.fuse.multithreaded = False
+
+        elif dsd == 'undef':
+            dsdcb = None
+        else:
+            raise ArgumentError, "key `dash_s_do': uninterpreted value " + str(dsd)
+
+        if dsdcb:
+            self.add_option('-s', action='callback', callback=dsdcb,
+                            help=SUPPRESS_HELP)
+
+
+    def exit(self, status=0, msg=None):
+        if msg:
+            sys.stderr.write(msg)
+
+    def error(self, msg):
+        SubbedOptParse.error(self, msg)
+        raise OptParseError, msg
+
+    def print_help(self, file=sys.stderr):
+        SubbedOptParse.print_help(self, file)
+        print >> file
+        self.fuse_args.setmod('showhelp')
+
+    def print_version(self, file=sys.stderr):
+        SubbedOptParse.print_version(self, file)
+        self.fuse_args.setmod('showversion')
+
+    def parse_args(self, args=None, values=None):
+        o, a = SubbedOptParse.parse_args(self, args, values)
+        if a and self.fetch_mp:
+            self.fuse_args.mountpoint = a.pop()
+        return o, a
+
+    def add_option(self, *opts, **attrs):
+        if 'mountopt' in attrs:
+            if opts or 'subopt' in attrs:
+                raise OptParseError(
+                  "having options or specifying the `subopt' attribute conflicts with `mountopt' attribute")
+            opts = ('-o',)
+            attrs['subopt'] = attrs.pop('mountopt')
+            if not 'dest' in attrs:
+                attrs['dest'] = attrs['subopt']
+
+        SubbedOptParse.add_option(self, *opts, **attrs)
+
+
+
+##########
+###
+###  The FUSE interface.
+###
+##########
+
+
+
+class ErrnoWrapper(object):
+
+    def __init__(self, func):
+        self.func = func
+
+    def __call__(self, *args, **kw):
+        try:
+            return apply(self.func, args, kw)
+        except (IOError, OSError), detail:
+            # Sometimes this is an int, sometimes an instance...
+            if hasattr(detail, "errno"): detail = detail.errno
+            return -detail
+
+
+########### Custom objects for transmitting system structures to FUSE
+
+class FuseStruct(object):
+
+    def __init__(self, **kw):
+        for k in kw:
+             setattr(self, k, kw[k])
+
+
+class Stat(FuseStruct):
+    """
+    Auxiliary class which can be filled up stat attributes.
+    The attributes are undefined by default.
+    """
+
+    def __init__(self, **kw):
+        self.st_mode  = None
+        self.st_ino   = 0
+        self.st_dev   = 0
+        self.st_nlink = None
+        self.st_uid   = 0
+        self.st_gid   = 0
+        self.st_size  = 0
+        self.st_atime = 0
+        self.st_mtime = 0
+        self.st_ctime = 0
+
+        FuseStruct.__init__(self, **kw)
+
+
+class StatVfs(FuseStruct):
+    """
+    Auxiliary class which can be filled up statvfs attributes.
+    The attributes are 0 by default.
+    """
+
+    def __init__(self, **kw):
+
+        self.f_bsize   = 0
+        self.f_frsize  = 0
+        self.f_blocks  = 0
+        self.f_bfree   = 0
+        self.f_bavail  = 0
+        self.f_files   = 0
+        self.f_ffree   = 0
+        self.f_favail  = 0
+        self.f_flag    = 0
+        self.f_namemax = 0
+
+        FuseStruct.__init__(self, **kw)
+
+
+class Direntry(FuseStruct):
+    """
+    Auxiliary class for carrying directory entry data.
+    Initialized with `name`. Further attributes (each
+    set to 0 as default):
+
+    offset
+        An integer (or long) parameter, used as a bookmark
+        during directory traversal.
+        This needs to be set it you want stateful directory
+        reading.
+
+    type
+       Directory entry type, should be one of the stat type
+       specifiers (stat.S_IFLNK, stat.S_IFBLK, stat.S_IFDIR,
+       stat.S_IFCHR, stat.S_IFREG, stat.S_IFIFO, stat.S_IFSOCK).
+
+    ino
+       Directory entry inode number.
+
+    Note that Python's standard directory reading interface is
+    stateless and provides only names, so the above optional
+    attributes doesn't make sense in that context.
+    """
+
+    def __init__(self, name, **kw):
+
+        self.name   = name
+        self.offset = 0
+        self.type   = 0
+        self.ino    = 0
+
+        FuseStruct.__init__(self, **kw)
+
+
+class Flock(FuseStruct):
+    """
+    Class for representing flock structures (cf. fcntl(3)).
+    
+    It makes sense to give values to the `l_type`, `l_start`,
+    `l_len`, `l_pid` attributes (`l_whence` is not used by
+    FUSE, see ``fuse.h``).
+    """
+
+    def __init__(self, name, **kw):
+    
+        self.l_type  = None
+        self.l_start = None
+        self.l_len   = None
+        self.l_pid   = None
+        FuseStruct.__init__(self, **kw)
+
+class Timespec(FuseStruct):
+    """
+    Cf. struct timespec in time.h:
+    http://www.opengroup.org/onlinepubs/009695399/basedefs/time.h.html
+    """
+
+    def __init__(self, name, **kw):
+    
+        self.tv_sec  = None
+        self.tv_nsec = None
+        FuseStruct.__init__(self, **kw)
+
+
+class FuseFileInfo(FuseStruct):
+
+    def __init__(self, **kw):
+
+        self.keep      = False
+        self.direct_io = False
+
+        FuseStruct.__init__(self, **kw)
+
+
+
+########## Interface for requiring certain features from your underlying FUSE library.
+
+def feature_needs(*feas):
+    """
+    Get info about the FUSE API version needed for the support of some features.
+
+    This function takes a variable number of feature patterns.
+
+    A feature pattern is either:
+
+    -  an integer (directly referring to a FUSE API version number)
+    -  a built-in feature specifier string (meaning defined by dictionary)
+    -  a string of the form ``has_foo``, where ``foo`` is a filesystem method
+       (refers to the API version where the method has been introduced)
+    -  a list/tuple of other feature patterns (matches each of its members)
+    -  a regexp (meant to be matched against the builtins plus ``has_foo``
+       patterns; can also be given by a string of the from "re:*")
+    -  a negated regexp (can be given by a string of the form "!re:*")
+
+    If called with no arguments, then the list of builtins is returned, mapped
+    to their meaning.
+
+    Otherwise the function returns the smallest FUSE API version number which
+    has all the matching features.
+
+    Builtin specifiers worth to explicit mention:
+    - ``stateful_files``: you want to use custom filehandles (eg. a file class).
+    - ``*``: you want all features.
+    - while ``has_foo`` makes sense for all filesystem method ``foo``, some
+      of these can be found among the builtins, too (the ones which can be
+      handled by the general rule).
+
+    specifiers like ``has_foo`` refer to requirement that the library knows of
+      the fs method ``foo``.
+    """
+
+    fmap = {'stateful_files': 22,
+            'stateful_dirs':  23,
+            'stateful_io':    ('stateful_files', 'stateful_dirs'),
+            'stateful_files_keep_cache': 23,
+            'stateful_files_direct_io': 23,
+            'keep_cache':     ('stateful_files_keep_cache',),
+            'direct_io':      ('stateful_files_direct_io',),
+            'has_opendir':    ('stateful_dirs',),
+            'has_releasedir': ('stateful_dirs',),
+            'has_fsyncdir':   ('stateful_dirs',),
+            'has_create':     25,
+            'has_access':     25,
+            'has_fgetattr':   25,
+            'has_ftruncate':  25,
+            'has_fsinit':     ('has_init'),
+            'has_fsdestroy':  ('has_destroy'),
+            'has_lock':       26,
+            'has_utimens':    26,
+            'has_bmap':       26,
+            'has_init':       23,
+            'has_destroy':    23,
+            '*':              '!re:^\*$'}
+
+    if not feas:
+        return fmap
+
+    def resolve(args, maxva):
+
+        for fp in args:
+            if isinstance(fp, int):
+                maxva[0] = max(maxva[0], fp)
+                continue
+            if isinstance(fp, list) or isinstance(fp, tuple):
+                for f in fp:
+                    yield f
+                continue
+            ma = isinstance(fp, str) and re.compile("(!\s*|)re:(.*)").match(fp)
+            if isinstance(fp, type(re.compile(''))) or ma:
+                neg = False
+                if ma:
+                    mag = ma.groups()
+                    fp = re.compile(mag[1])
+                    neg = bool(mag[0])
+                for f in fmap.keys() + [ 'has_' + a for a in Fuse._attrs ]:
+                    if neg != bool(re.search(fp, f)):
+                        yield f
+                continue
+            ma = re.compile("has_(.*)").match(fp)
+            if ma and ma.groups()[0] in Fuse._attrs and not fp in fmap:
+                yield 21
+                continue
+            yield fmap[fp]
+
+    maxva = [0]
+    while feas:
+        feas = set(resolve(feas, maxva))
+
+    return maxva[0]
+
+
+def APIVersion():
+    """Get the API version of your underlying FUSE lib"""
+
+    return FuseAPIVersion()
+
+
+def feature_assert(*feas):
+    """
+    Takes some feature patterns (like in `feature_needs`).
+    Raises a fuse.FuseError if your underlying FUSE lib fails
+    to have some of the matching features.
+
+    (Note: use a ``has_foo`` type feature assertion only if lib support
+    for method ``foo`` is *necessary* for your fs. Don't use this assertion
+    just because your fs implements ``foo``. The usefulness of ``has_foo``
+    is limited by the fact that we can't guarantee that your FUSE kernel
+    module also supports ``foo``.)
+    """
+
+    fav = APIVersion()
+
+    for fea in feas:
+        fn = feature_needs(fea)
+        if fav < fn:
+            raise FuseError(
+              "FUSE API version %d is required for feature `%s' but only %d is available" % \
+              (fn, str(fea), fav))
+
+
+############# Subclass this.
+
+class Fuse(object):
+    """
+    Python interface to FUSE.
+
+    Basic usage:
+
+    - instantiate
+
+    - add options to `parser` attribute (an instance of `FuseOptParse`)
+
+    - call `parse`
+
+    - call `main`
+    """
+
+    _attrs = ['getattr', 'readlink', 'readdir', 'mknod', 'mkdir',
+              'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
+              'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
+              'statfs', 'fsync', 'create', 'opendir', 'releasedir', 'fsyncdir',
+              'flush', 'fgetattr', 'ftruncate', 'getxattr', 'listxattr',
+              'setxattr', 'removexattr', 'access', 'lock', 'utimens', 'bmap',
+              'fsinit', 'fsdestroy']
+
+    fusage = "%prog [mountpoint] [options]"
+
+    def __init__(self, *args, **kw):
+        """
+        Not much happens here apart from initializing the `parser` attribute.
+        Arguments are forwarded to the constructor of the parser class almost
+        unchanged.
+
+        The parser class is `FuseOptParse` unless you specify one using the
+        ``parser_class`` keyword. (See `FuseOptParse` documentation for
+        available options.)
+        """
+
+        if not fuse_python_api:
+            raise RuntimeError, __name__ + """.fuse_python_api not defined.
+
+! Please define """ + __name__ + """.fuse_python_api internally (eg.
+! 
+! (1)  """ + __name__ + """.fuse_python_api = """ + `FUSE_PYTHON_API_VERSION` + """
+! 
+! ) or in the enviroment (eg. 
+! 
+! (2)  FUSE_PYTHON_API=0.1
+! 
+! ).
+!
+! If you are actually developing a filesystem, probably (1) is the way to go.
+! If you are using a filesystem written before 2007 Q2, probably (2) is what
+! you want."
+"""
+
+        def malformed():
+            raise RuntimeError, \
+                  "malformatted fuse_python_api value " + `fuse_python_api`
+        if not isinstance(fuse_python_api, tuple):
+            malformed()
+        for i in fuse_python_api:
+            if not isinstance(i, int) or i < 0:
+                malformed() 
+
+        if fuse_python_api > FUSE_PYTHON_API_VERSION:
+            raise RuntimeError, """
+! You require FUSE-Python API version """ + `fuse_python_api` + """.
+! However, the latest available is """ + `FUSE_PYTHON_API_VERSION` + """.
+"""
+
+        self.fuse_args = \
+            'fuse_args' in kw and kw.pop('fuse_args') or FuseArgs()
+
+        if get_compat_0_1():
+            return self.__init_0_1__(*args, **kw)
+
+        self.multithreaded = True
+
+        if not 'usage' in kw:
+            kw['usage'] = self.fusage
+        if not 'fuse_args' in kw:
+            kw['fuse_args'] = self.fuse_args
+        kw['fuse'] = self
+        parserclass = \
+          'parser_class' in kw and kw.pop('parser_class') or FuseOptParse
+
+        self.parser = parserclass(*args, **kw)
+        self.methproxy = self.Methproxy()
+
+    def parse(self, *args, **kw):
+        """Parse command line, fill `fuse_args` attribute."""
+
+        ev = 'errex' in kw and kw.pop('errex')
+        if ev and not isinstance(ev, int):
+            raise TypeError, "error exit value should be an integer"
+
+        try:
+            self.cmdline = self.parser.parse_args(*args, **kw)
+        except OptParseError:
+          if ev:
+              sys.exit(ev)
+          raise
+
+        return self.fuse_args
+
+    def main(self, args=None):
+        """Enter filesystem service loop."""
+
+        if get_compat_0_1():
+            args = self.main_0_1_preamble()
+
+        d = {'multithreaded': self.multithreaded and 1 or 0}
+        d['fuse_args'] = args or self.fuse_args.assemble()
+
+        for t in 'file_class', 'dir_class':
+            if hasattr(self, t):
+                getattr(self.methproxy, 'set_' + t)(getattr(self,t))
+
+        for a in self._attrs:
+            b = a
+            if get_compat_0_1() and a in self.compatmap:
+                b = self.compatmap[a]
+            if hasattr(self, b):
+                c = ''
+                if get_compat_0_1() and hasattr(self, a + '_compat_0_1'):
+                    c = '_compat_0_1'
+                d[a] = ErrnoWrapper(self.lowwrap(a + c))
+
+        try:
+            main(**d)
+        except FuseError:
+            if args or self.fuse_args.mount_expected():
+                raise
+
+    def lowwrap(self, fname):
+        """
+        Wraps the fname method when the C code expects a different kind of
+        callback than we have in the fusepy API. (The wrapper is usually for
+        performing some checks or transfromations which could be done in C but
+        is simpler if done in Python.)
+
+        Currently `open` and `create` are wrapped: a boolean flag is added
+        which indicates if the result is to be kept during the opened file's
+        lifetime or can be thrown away. Namely, it's considered disposable
+        if it's an instance of FuseFileInfo.
+        """
+        fun = getattr(self, fname)
+
+        if fname in ('open', 'create'):
+            def wrap(*a, **kw):
+                res = fun(*a, **kw)
+                if not res or type(res) == type(0):
+                    return res
+                else:
+                    return (res, type(res) != FuseFileInfo)
+        elif fname == 'utimens':
+            def wrap(path, acc_sec, acc_nsec, mod_sec, mod_nsec):
+                ts_acc = Timespec(tv_sec = acc_sec, tv_nsec = acc_nsec)
+                ts_mod = Timespec(tv_sec = mod_sec, tv_nsec = mod_nsec)
+                return fun(path, ts_acc, ts_mod)
+        else:
+            wrap = fun
+
+        return wrap
+
+    def GetContext(self):
+        return FuseGetContext(self)
+
+    def Invalidate(self, path):
+        return FuseInvalidate(self, path)
+
+    def fuseoptref(cls):
+        """
+        Find out which options are recognized by the library.
+        Result is a `FuseArgs` instance with the list of supported
+        options, suitable for passing on to the `filter` method of
+        another `FuseArgs` instance.
+        """
+
+        import os, re
+
+        pr, pw = os.pipe()
+        pid = os.fork()
+        if pid == 0:
+             os.dup2(pw, 2)
+             os.close(pr)
+
+             fh = cls()
+             fh.fuse_args = FuseArgs()
+             fh.fuse_args.setmod('showhelp')
+             fh.main()
+             sys.exit()
+
+        os.close(pw)
+
+        fa = FuseArgs()
+        ore = re.compile("-o\s+([\w\[\]]+(?:=\w+)?)")
+        fpr = os.fdopen(pr)
+        for l in fpr:
+             m = ore.search(l)
+             if m:
+                 o = m.groups()[0]
+                 oa = [o]
+                 # try to catch two-in-one options (like "[no]foo")
+                 opa = o.split("[")
+                 if len(opa) == 2:
+                    o1, ox = opa
+                    oxpa = ox.split("]")
+                    if len(oxpa) == 2:
+                       oo, o2 = oxpa
+                       oa = [o1 + o2, o1 + oo + o2]
+                 for o in oa:
+                     fa.add(o)
+
+        fpr.close()
+        return fa
+
+    fuseoptref = classmethod(fuseoptref)
+
+
+    class Methproxy(object):
+
+        def __init__(self):
+
+            class mpx(object):
+               def __init__(self, name):
+                   self.name = name
+               def __call__(self, *a, **kw):
+                   return getattr(a[-1], self.name)(*(a[1:-1]), **kw)
+
+            self.proxyclass = mpx
+            self.mdic = {}
+            self.file_class = None
+            self.dir_class = None
+
+        def __call__(self, meth):
+            return meth in self.mdic and self.mdic[meth] or None
+
+        def _add_class_type(cls, type, inits, proxied):
+
+            def setter(self, xcls):
+
+                setattr(self, type + '_class', xcls)
+
+                for m in inits:
+                    self.mdic[m] = xcls
+
+                for m in proxied:
+                    if hasattr(xcls, m):
+                        self.mdic[m] = self.proxyclass(m)
+
+            setattr(cls, 'set_' + type + '_class', setter)
+
+        _add_class_type = classmethod(_add_class_type)
+
+    Methproxy._add_class_type('file', ('open', 'create'),
+                              ('read', 'write', 'fsync', 'release', 'flush',
+                               'fgetattr', 'ftruncate', 'lock'))
+    Methproxy._add_class_type('dir', ('opendir',),
+                              ('readdir', 'fsyncdir', 'releasedir'))
+
+
+    def __getattr__(self, meth):
+
+        m = self.methproxy(meth)
+        if m:
+            return m
+
+        raise AttributeError, "Fuse instance has no attribute '%s'" % meth
+
+
+
+##########
+###
+###  Compat stuff.
+###
+##########
+
+
+
+    def __init_0_1__(self, *args, **kw):
+
+        self.flags = 0
+        multithreaded = 0
+
+        # default attributes
+        if args == ():
+            # there is a self.optlist.append() later on, make sure it won't
+            # bomb out.
+            self.optlist = []
+        else:
+            self.optlist = args
+        self.optdict = kw
+
+        if len(self.optlist) == 1:
+            self.mountpoint = self.optlist[0]
+        else:
+            self.mountpoint = None
+
+        # grab command-line arguments, if any.
+        # Those will override whatever parameters
+        # were passed to __init__ directly.
+        argv = sys.argv
+        argc = len(argv)
+        if argc > 1:
+            # we've been given the mountpoint
+            self.mountpoint = argv[1]
+        if argc > 2:
+            # we've received mount args
+            optstr = argv[2]
+            opts = optstr.split(",")
+            for o in opts:
+                try:
+                    k, v = o.split("=", 1)
+                    self.optdict[k] = v
+                except:
+                    self.optlist.append(o)
+
+
+    def main_0_1_preamble(self):
+
+        cfargs = FuseArgs()
+
+        cfargs.mountpoint = self.mountpoint
+
+        if hasattr(self, 'debug'):
+            cfargs.add('debug')
+
+        if hasattr(self, 'allow_other'):
+            cfargs.add('allow_other')
+
+        if hasattr(self, 'kernel_cache'):
+            cfargs.add('kernel_cache')
+
+        return cfargs.assemble()
+
+
+    def getattr_compat_0_1(self, *a):
+        from os import stat_result
+
+        return stat_result(self.getattr(*a))
+
+
+    def statfs_compat_0_1(self, *a):
+
+        oout = self.statfs(*a)
+        lo = len(oout)
+
+        svf = StatVfs()
+        svf.f_bsize   = oout[0]                   # 0
+        svf.f_frsize  = oout[lo >= 8 and 7 or 0]  # 1
+        svf.f_blocks  = oout[1]                   # 2
+        svf.f_bfree   = oout[2]                   # 3
+        svf.f_bavail  = oout[3]                   # 4
+        svf.f_files   = oout[4]                   # 5
+        svf.f_ffree   = oout[5]                   # 6
+        svf.f_favail  = lo >= 9 and oout[8] or 0  # 7
+        svf.f_flag    = lo >= 10 and oout[9] or 0 # 8
+        svf.f_namemax = oout[6]                   # 9
+
+        return svf
+
+
+    def readdir_compat_0_1(self, path, offset, *fh):
+
+        for name, type in self.getdir(path):
+            de = Direntry(name)
+            de.type = type
+
+            yield de
+
+
+    compatmap = {'readdir': 'getdir'}
diff --git a/mac/fuseparts/__init__.py b/mac/fuseparts/__init__.py
new file mode 100644 (file)
index 0000000..0988857
--- /dev/null
@@ -0,0 +1 @@
+__version__ = "0.2"
diff --git a/mac/fuseparts/_fusemodule.so b/mac/fuseparts/_fusemodule.so
new file mode 100644 (file)
index 0000000..c802be4
Binary files /dev/null and b/mac/fuseparts/_fusemodule.so differ
diff --git a/mac/fuseparts/setcompatwrap.py b/mac/fuseparts/setcompatwrap.py
new file mode 100644 (file)
index 0000000..7ead738
--- /dev/null
@@ -0,0 +1,5 @@
+try:
+  set()
+  set = set
+except:
+  from sets import Set as set
diff --git a/mac/fuseparts/subbedopts.py b/mac/fuseparts/subbedopts.py
new file mode 100644 (file)
index 0000000..9cf40ae
--- /dev/null
@@ -0,0 +1,268 @@
+#
+#    Copyright (C) 2006  Csaba Henk  <csaba.henk@creo.hu>
+#
+#    This program can be distributed under the terms of the GNU LGPL.
+#    See the file COPYING.
+#
+
+from optparse import Option, OptionParser, OptParseError, OptionConflictError
+from optparse import HelpFormatter, IndentedHelpFormatter, SUPPRESS_HELP
+from fuseparts.setcompatwrap import set
+
+##########
+###
+###  Generic suboption parsing stuff.
+###
+##########
+
+
+
+class SubOptsHive(object):
+    """
+    Class for collecting unhandled suboptions.
+    """
+
+    def __init__(self):
+
+        self.optlist = set()
+        self.optdict = {}
+
+    def _str_core(self):
+
+        sa = []
+        for k, v in self.optdict.iteritems():
+             sa.append(str(k) + '=' + str(v))
+
+        ra = (list(self.optlist) + sa) or ["(none)"]
+        ra.sort()
+        return ra 
+
+    def __str__(self):
+        return "< opts: " + ", ".join(self._str_core()) + " >"
+
+    def canonify(self):
+        """
+        Transform self to an equivalent canonical form:
+        delete optdict keys with False value, move optdict keys
+        with True value to optlist, stringify other values.
+        """
+
+        for k, v in self.optdict.iteritems():
+            if v == False:
+                self.optdict.pop(k)
+            elif v == True:
+                self.optdict.pop(k)
+                self.optlist.add(v)
+            else:
+                self.optdict[k] = str(v)
+
+    def filter(self, other):
+        """
+        Throw away those options which are not in the other one.
+        Returns a new instance with the rejected options.
+        """
+
+        self.canonify()
+        other.canonify()
+
+        rej = self.__class__()
+        rej.optlist = self.optlist.difference(other.optlist)
+        self.optlist.difference_update(rej.optlist)
+        for x in self.optdict.copy():
+             if x not in other.optdict:
+                 self.optdict.pop(x)
+                 rej.optdict[x] = None
+
+        return rej
+
+    def add(self, opt, val=None):
+        """Add a suboption."""
+
+        ov = opt.split('=', 1)
+        o = ov[0]
+        v = len(ov) > 1 and ov[1] or None
+
+        if (v):
+            if val != None:
+                raise AttributeError, "ambiguous option value"
+            val = v
+
+        if val == False:
+            return
+
+        if val in (None, True):
+            self.optlist.add(o)
+        else:
+            self.optdict[o] = val
+
+
+
+class SubbedOpt(Option):
+    """
+    `Option` derivative enhanced with the attribute of being a suboption of
+     some other option (like ``foo`` and ``bar`` for ``-o`` in ``-o foo,bar``).
+    """
+
+    ATTRS = Option.ATTRS + ["subopt", "subsep", "subopts_hive"]
+    ACTIONS = Option.ACTIONS + ("store_hive",)
+    STORE_ACTIONS = Option.STORE_ACTIONS + ("store_hive",)
+    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("store_hive",)
+
+    def __init__(self, *opts, **attrs):
+
+       self.subopt_map = {}
+
+       if "subopt" in attrs:
+           self._short_opts = []
+           self._long_opts = []
+           self._set_opt_strings(opts)
+           self.baseopt = self._short_opts[0] or self._long_opts[0]
+           opts = ()
+
+       Option.__init__(self, *opts, **attrs)
+
+    def __str__(self):
+        pf = ""
+        if hasattr(self, "subopt") and self.subopt:
+            pf = " %s...,%s,..." % (self.baseopt, self.subopt)
+        return Option.__str__(self) + pf
+
+    def _check_opt_strings(self, opts):
+        return opts
+
+    def _check_dest(self):
+        try:
+            Option._check_dest(self)
+        except IndexError:
+            if self.subopt:
+                self.dest = "__%s__%s" % (self.baseopt, self.subopt)
+                self.dest = self.dest.replace("-", "")
+            else:
+                raise
+
+    def get_opt_string(self):
+        if hasattr(self, 'subopt'):
+            return self.subopt
+        else:
+            return Option.get_opt_string(self)
+
+    def take_action(self, action, dest, opt, value, values, parser):
+        if action == "store_hive":
+            if not hasattr(values, dest) or getattr(values, dest) == None:
+                 if hasattr(self, "subopts_hive") and self.subopts_hive:
+                     hive = self.subopts_hive
+                 else:
+                     hive = parser.hive_class()
+                 setattr(values, dest, hive)
+            for o in value.split(self.subsep or ","):
+                oo = o.split('=')
+                ok = oo[0]
+                ov = None
+                if (len(oo) > 1):
+                    ov = oo[1]
+                if ok in self.subopt_map:
+                    self.subopt_map[ok].process(ok, ov, values, parser)
+                else:
+                    getattr(values, dest).add(*oo)
+            return
+        Option.take_action(self, action, dest, opt, value, values, parser)
+
+    def register_sub(self, o):
+        """Register argument a suboption for `self`."""
+
+        if o.subopt in self.subopt_map:
+            raise OptionConflictError(
+              "conflicting suboption handlers for `%s'" % o.subopt,
+              o)
+        self.subopt_map[o.subopt] = o
+
+    CHECK_METHODS = []
+    for m in Option.CHECK_METHODS:
+        #if not m == Option._check_dest:
+        if not m.__name__ == '_check_dest':
+            CHECK_METHODS.append(m)
+    CHECK_METHODS.append(_check_dest)
+
+
+
+class SubbedOptFormatter(HelpFormatter):
+
+    def format_option_strings(self, option):
+        if hasattr(option, "subopt") and option.subopt:
+            res = '-o ' + option.subopt
+            if option.takes_value():
+                res += "="
+                res += option.metavar or 'FOO'
+            return res
+
+        return HelpFormatter.format_option_strings(self, option)
+
+
+
+class SubbedOptIndentedFormatter(IndentedHelpFormatter, SubbedOptFormatter):
+
+    def format_option_strings(self, option):
+        return SubbedOptFormatter.format_option_strings(self, option)
+
+
+
+class SubbedOptParse(OptionParser):
+    """
+    This class alters / enhances `OptionParser` with *suboption handlers*.
+
+    That is, calling `sop.add_option('-x', subopt=foo)` installs a handler
+    which will be triggered if there is ``-x foo`` in the command line being
+    parsed (or, eg., ``-x foo,bar``).
+
+    Moreover, ``-x`` implicitly gets a handler which collects the unhandled
+    suboptions of ``-x`` into a `SubOptsHive` instance (accessible post festam
+    via the `x` attribute of the returned Values object). (The only exception
+    is when ``-x`` has *explicitly*  been added with action ``store_hive``.
+    This opens up the possibility of customizing the ``-x`` handler at some
+    rate.)
+
+    Suboption handlers have all the nice features of normal option handlers,
+    eg. they are displayed in the automatically generated help message
+    (and can have their own help info).
+    """
+
+    def __init__(self, *args, **kw):
+
+         if not 'formatter' in kw:
+             kw['formatter'] = SubbedOptIndentedFormatter()
+         if not 'option_class' in kw:
+             kw['option_class'] = SubbedOpt
+         if 'hive_class' in kw:
+             self.hive_class = kw.pop('hive_class')
+         else:
+             self.hive_class = SubOptsHive
+
+         OptionParser.__init__(self, *args, **kw)
+
+    def add_option(self, *args, **kwargs):
+        if 'action' in kwargs and kwargs['action'] == 'store_hive':
+            if 'subopt' in kwargs:
+                raise OptParseError(
+                  """option can't have a `subopt' attr and `action="store_hive"' at the same time""")
+            if not 'type' in kwargs:
+                kwargs['type'] = 'string'
+        elif 'subopt' in kwargs:
+            o = self.option_class(*args, **kwargs)
+
+            oo = self.get_option(o.baseopt)
+            if oo:
+                if oo.action != "store_hive":
+                    raise OptionConflictError(
+                      "can't add subopt as option has already a handler that doesn't do `store_hive'",
+                      oo)
+            else:
+                self.add_option(o.baseopt, action='store_hive',
+                                metavar="sub1,[sub2,...]")
+                oo = self.get_option(o.baseopt)
+
+            oo.register_sub(o)
+
+            args = (o,)
+            kwargs = {}
+
+        return OptionParser.add_option(self, *args, **kwargs)
diff --git a/mac/macfuse/fuse.py b/mac/macfuse/fuse.py
deleted file mode 100644 (file)
index 51f602b..0000000
+++ /dev/null
@@ -1,997 +0,0 @@
-#
-#    Copyright (C) 2001  Jeff Epler  <jepler@unpythonic.dhs.org>
-#    Copyright (C) 2006  Csaba Henk  <csaba.henk@creo.hu>
-#
-#    This program can be distributed under the terms of the GNU LGPL.
-#    See the file COPYING.
-#
-
-
-# suppress version mismatch warnings
-try:
-    import warnings
-    warnings.filterwarnings('ignore',
-                            'Python C API version mismatch',
-                            RuntimeWarning,
-                            )
-except:
-    pass
-
-from string import join
-import sys
-from errno import *
-from os import environ
-import re
-from fuseparts import __version__
-from fuseparts._fuse import main, FuseGetContext, FuseInvalidate
-from fuseparts._fuse import FuseError, FuseAPIVersion
-from fuseparts.subbedopts import SubOptsHive, SubbedOptFormatter
-from fuseparts.subbedopts import SubbedOptIndentedFormatter, SubbedOptParse
-from fuseparts.subbedopts import SUPPRESS_HELP, OptParseError
-from fuseparts.setcompatwrap import set
-
-
-##########
-###
-###  API specification API.
-###
-##########
-
-# The actual API version of this module
-FUSE_PYTHON_API_VERSION = (0, 2)
-
-def __getenv__(var, pattern = '.', trans = lambda x: x):
-    """
-    Fetch enviroment variable and optionally transform it. Return `None` if
-    variable is unset. Bail out if value of variable doesn't match (optional)
-    regex pattern.
-    """
-
-    if var not in environ:
-        return None
-    val = environ[var]
-    rpat = pattern
-    if not isinstance(rpat, type(re.compile(''))):
-        rpat = re.compile(rpat)
-    if not rpat.search(val):
-        raise RuntimeError("env var %s doesn't match required pattern %s" % \
-                           (var, `pattern`))
-    return trans(val)
-
-def get_fuse_python_api():
-    if fuse_python_api:
-        return fuse_python_api
-    elif compat_0_1:
-        # deprecated way of API specification
-        return (0,1)
-
-def get_compat_0_1():
-    return get_fuse_python_api() == (0, 1)
-
-# API version to be used
-fuse_python_api = __getenv__('FUSE_PYTHON_API', '^[\d.]+$',
-                              lambda x: tuple([int(i) for i in x.split('.')]))
-
-# deprecated way of API specification
-compat_0_1 = __getenv__('FUSE_PYTHON_COMPAT', '^(0.1|ALL)$', lambda x: True)
-
-fuse_python_api = get_fuse_python_api()
-
-##########
-###
-###  Parsing for FUSE.
-###
-##########
-
-
-
-class FuseArgs(SubOptsHive):
-    """
-    Class representing a FUSE command line.
-    """
-
-    fuse_modifiers = {'showhelp': '-ho',
-                      'showversion': '-V',
-                      'foreground': '-f'}
-
-    def __init__(self):
-
-        SubOptsHive.__init__(self)
-
-        self.modifiers = {}
-        self.mountpoint = None
-
-        for m in self.fuse_modifiers:
-            self.modifiers[m] = False
-
-    def __str__(self):
-        return '\n'.join(['< on ' + str(self.mountpoint) + ':',
-                          '  ' + str(self.modifiers), '  -o ']) + \
-               ',\n     '.join(self._str_core()) + \
-               ' >'
-
-    def getmod(self, mod):
-        return self.modifiers[mod]
-
-    def setmod(self, mod):
-        self.modifiers[mod] = True
-
-    def unsetmod(self, mod):
-        self.modifiers[mod] = False
-
-    def mount_expected(self):
-        if self.getmod('showhelp'):
-            return False
-        if self.getmod('showversion'):
-            return False
-        return True
-
-    def assemble(self):
-        """Mangle self into an argument array"""
-
-        self.canonify()
-        args = [sys.argv and sys.argv[0] or "python"]
-        if self.mountpoint:
-            args.append(self.mountpoint)
-        for m, v in self.modifiers.iteritems():
-            if v:
-                args.append(self.fuse_modifiers[m])
-
-        opta = []
-        for o, v in self.optdict.iteritems():
-                opta.append(o + '=' + v)
-        opta.extend(self.optlist)
-
-        if opta:
-            args.append("-o" + ",".join(opta))
-
-        return args
-
-    def filter(self, other=None):
-        """
-        Same as for SubOptsHive, with the following difference:
-        if other is not specified, `Fuse.fuseoptref()` is run and its result
-        will be used.
-        """
-
-        if not other:
-            other = Fuse.fuseoptref()
-
-        return SubOptsHive.filter(self, other)
-
-
-
-class FuseFormatter(SubbedOptIndentedFormatter):
-
-    def __init__(self, **kw):
-        if not 'indent_increment' in kw:
-            kw['indent_increment'] = 4
-        SubbedOptIndentedFormatter.__init__(self, **kw)
-
-    def store_option_strings(self, parser):
-        SubbedOptIndentedFormatter.store_option_strings(self, parser)
-        # 27 is how the lib stock help appears
-        self.help_position = max(self.help_position, 27)
-        self.help_width = self.width - self.help_position
-
-
-class FuseOptParse(SubbedOptParse):
-    """
-    This class alters / enhances `SubbedOptParse` so that it's
-    suitable for usage with FUSE.
-
-    - When adding options, you can use the `mountopt` pseudo-attribute which
-      is equivalent with adding a subopt for option ``-o``
-      (it doesn't require an option argument).
-
-    - FUSE compatible help and version printing.
-
-    - Error and exit callbacks are relaxed. In case of FUSE, the command
-      line is to be treated as a DSL [#]_. You don't wanna this module to
-      force an exit on you just because you hit a DSL syntax error.
-
-    - Built-in support for conventional FUSE options (``-d``, ``-f`, ``-s``).
-      The way of this can be tuned by keyword arguments, see below.
-
-    .. [#] http://en.wikipedia.org/wiki/Domain-specific_programming_language
-
-    Keyword arguments for initialization
-    ------------------------------------
-
-    standard_mods
-      Boolean [default is `True`].
-      Enables support for the usual interpretation of the ``-d``, ``-f``
-      options.
-
-    fetch_mp
-      Boolean [default is `True`].
-      If it's True, then the last (non-option) argument
-      (if there is such a thing) will be used as the FUSE mountpoint.
-
-    dash_s_do
-      String: ``whine``, ``undef``, or ``setsingle`` [default is ``whine``].
-      The ``-s`` option -- traditionally for asking for single-threadedness --
-      is an oddball: single/multi threadedness of a fuse-py fs doesn't depend
-      on the FUSE command line, we have direct control over it.
-
-      Therefore we have two conflicting principles:
-
-      - *Orthogonality*: option parsing shouldn't affect the backing `Fuse`
-        instance directly, only via its `fuse_args` attribute.
-
-      - *POLS*: behave like other FUSE based fs-es do. The stock FUSE help
-        makes mention of ``-s`` as a single-threadedness setter.
-
-      So, if we follow POLS and implement a conventional ``-s`` option, then
-      we have to go beyond the `fuse_args` attribute and set the respective
-      Fuse attribute directly, hence violating orthogonality.
-
-      We let the fs authors make their choice: ``dash_s_do=undef`` leaves this
-      option unhandled, and the fs author can add a handler as she desires.
-      ``dash_s_do=setsingle`` enables the traditional behaviour.
-
-      Using ``dash_s_do=setsingle`` is not problematic at all, but we want fs
-      authors be aware of the particularity of ``-s``, therefore the default is
-      the ``dash_s_do=whine`` setting which raises an exception for ``-s`` and
-      suggests the user to read this documentation.
-
-    dash_o_handler
-      Argument should be a SubbedOpt instance (created with
-      ``action="store_hive"`` if you want it to be useful).
-      This lets you customize the handler of the ``-o`` option. For example,
-      you can alter or suppress the generic ``-o`` entry in help output.
-    """
-
-    def __init__(self, *args, **kw):
-
-        self.mountopts = []
-
-        self.fuse_args = \
-            'fuse_args' in kw and kw.pop('fuse_args') or FuseArgs()
-        dsd = 'dash_s_do' in kw and kw.pop('dash_s_do') or 'whine'
-        if 'fetch_mp' in kw:
-            self.fetch_mp = bool(kw.pop('fetch_mp'))
-        else:
-            self.fetch_mp = True
-        if 'standard_mods' in kw:
-            smods = bool(kw.pop('standard_mods'))
-        else:
-            smods = True
-        if 'fuse' in kw:
-            self.fuse = kw.pop('fuse')
-        if not 'formatter' in kw:
-            kw['formatter'] = FuseFormatter()
-        doh = 'dash_o_handler' in kw and kw.pop('dash_o_handler')
-
-        SubbedOptParse.__init__(self, *args, **kw)
-
-        if doh:
-            self.add_option(doh)
-        else:
-            self.add_option('-o', action='store_hive',
-                            subopts_hive=self.fuse_args, help="mount options",
-                            metavar="opt,[opt...]")
-
-        if smods:
-            self.add_option('-f', action='callback',
-                            callback=lambda *a: self.fuse_args.setmod('foreground'),
-                            help=SUPPRESS_HELP)
-            self.add_option('-d', action='callback',
-                            callback=lambda *a: self.fuse_args.add('debug'),
-                            help=SUPPRESS_HELP)
-
-        if dsd == 'whine':
-            def dsdcb(option, opt_str, value, parser):
-                raise RuntimeError, """
-
-! If you want the "-s" option to work, pass
-!
-!   dash_s_do='setsingle'
-!
-! to the Fuse constructor. See docstring of the FuseOptParse class for an
-! explanation why is it not set by default.
-"""
-
-        elif dsd == 'setsingle':
-            def dsdcb(option, opt_str, value, parser):
-                self.fuse.multithreaded = False
-
-        elif dsd == 'undef':
-            dsdcb = None
-        else:
-            raise ArgumentError, "key `dash_s_do': uninterpreted value " + str(dsd)
-
-        if dsdcb:
-            self.add_option('-s', action='callback', callback=dsdcb,
-                            help=SUPPRESS_HELP)
-
-
-    def exit(self, status=0, msg=None):
-        if msg:
-            sys.stderr.write(msg)
-
-    def error(self, msg):
-        SubbedOptParse.error(self, msg)
-        raise OptParseError, msg
-
-    def print_help(self, file=sys.stderr):
-        SubbedOptParse.print_help(self, file)
-        print >> file
-        self.fuse_args.setmod('showhelp')
-
-    def print_version(self, file=sys.stderr):
-        SubbedOptParse.print_version(self, file)
-        self.fuse_args.setmod('showversion')
-
-    def parse_args(self, args=None, values=None):
-        o, a = SubbedOptParse.parse_args(self, args, values)
-        if a and self.fetch_mp:
-            self.fuse_args.mountpoint = a.pop()
-        return o, a
-
-    def add_option(self, *opts, **attrs):
-        if 'mountopt' in attrs:
-            if opts or 'subopt' in attrs:
-                raise OptParseError(
-                  "having options or specifying the `subopt' attribute conflicts with `mountopt' attribute")
-            opts = ('-o',)
-            attrs['subopt'] = attrs.pop('mountopt')
-            if not 'dest' in attrs:
-                attrs['dest'] = attrs['subopt']
-
-        SubbedOptParse.add_option(self, *opts, **attrs)
-
-
-
-##########
-###
-###  The FUSE interface.
-###
-##########
-
-
-
-class ErrnoWrapper(object):
-
-    def __init__(self, func):
-        self.func = func
-
-    def __call__(self, *args, **kw):
-        try:
-            return apply(self.func, args, kw)
-        except (IOError, OSError), detail:
-            # Sometimes this is an int, sometimes an instance...
-            if hasattr(detail, "errno"): detail = detail.errno
-            return -detail
-
-
-########### Custom objects for transmitting system structures to FUSE
-
-class FuseStruct(object):
-
-    def __init__(self, **kw):
-        for k in kw:
-             setattr(self, k, kw[k])
-
-
-class Stat(FuseStruct):
-    """
-    Auxiliary class which can be filled up stat attributes.
-    The attributes are undefined by default.
-    """
-
-    def __init__(self, **kw):
-        self.st_mode  = None
-        self.st_ino   = 0
-        self.st_dev   = 0
-        self.st_nlink = None
-        self.st_uid   = 0
-        self.st_gid   = 0
-        self.st_size  = 0
-        self.st_atime = 0
-        self.st_mtime = 0
-        self.st_ctime = 0
-
-        FuseStruct.__init__(self, **kw)
-
-
-class StatVfs(FuseStruct):
-    """
-    Auxiliary class which can be filled up statvfs attributes.
-    The attributes are 0 by default.
-    """
-
-    def __init__(self, **kw):
-
-        self.f_bsize   = 0
-        self.f_frsize  = 0
-        self.f_blocks  = 0
-        self.f_bfree   = 0
-        self.f_bavail  = 0
-        self.f_files   = 0
-        self.f_ffree   = 0
-        self.f_favail  = 0
-        self.f_flag    = 0
-        self.f_namemax = 0
-
-        FuseStruct.__init__(self, **kw)
-
-
-class Direntry(FuseStruct):
-    """
-    Auxiliary class for carrying directory entry data.
-    Initialized with `name`. Further attributes (each
-    set to 0 as default):
-
-    offset
-        An integer (or long) parameter, used as a bookmark
-        during directory traversal.
-        This needs to be set it you want stateful directory
-        reading.
-
-    type
-       Directory entry type, should be one of the stat type
-       specifiers (stat.S_IFLNK, stat.S_IFBLK, stat.S_IFDIR,
-       stat.S_IFCHR, stat.S_IFREG, stat.S_IFIFO, stat.S_IFSOCK).
-
-    ino
-       Directory entry inode number.
-
-    Note that Python's standard directory reading interface is
-    stateless and provides only names, so the above optional
-    attributes doesn't make sense in that context.
-    """
-
-    def __init__(self, name, **kw):
-
-        self.name   = name
-        self.offset = 0
-        self.type   = 0
-        self.ino    = 0
-
-        FuseStruct.__init__(self, **kw)
-
-
-class Flock(FuseStruct):
-    """
-    Class for representing flock structures (cf. fcntl(3)).
-    
-    It makes sense to give values to the `l_type`, `l_start`,
-    `l_len`, `l_pid` attributes (`l_whence` is not used by
-    FUSE, see ``fuse.h``).
-    """
-
-    def __init__(self, name, **kw):
-    
-        self.l_type  = None
-        self.l_start = None
-        self.l_len   = None
-        self.l_pid   = None
-        FuseStruct.__init__(self, **kw)
-
-class Timespec(FuseStruct):
-    """
-    Cf. struct timespec in time.h:
-    http://www.opengroup.org/onlinepubs/009695399/basedefs/time.h.html
-    """
-
-    def __init__(self, name, **kw):
-    
-        self.tv_sec  = None
-        self.tv_nsec = None
-        FuseStruct.__init__(self, **kw)
-
-
-class FuseFileInfo(FuseStruct):
-
-    def __init__(self, **kw):
-
-        self.keep      = False
-        self.direct_io = False
-
-        FuseStruct.__init__(self, **kw)
-
-
-
-########## Interface for requiring certain features from your underlying FUSE library.
-
-def feature_needs(*feas):
-    """
-    Get info about the FUSE API version needed for the support of some features.
-
-    This function takes a variable number of feature patterns.
-
-    A feature pattern is either:
-
-    -  an integer (directly referring to a FUSE API version number)
-    -  a built-in feature specifier string (meaning defined by dictionary)
-    -  a string of the form ``has_foo``, where ``foo`` is a filesystem method
-       (refers to the API version where the method has been introduced)
-    -  a list/tuple of other feature patterns (matches each of its members)
-    -  a regexp (meant to be matched against the builtins plus ``has_foo``
-       patterns; can also be given by a string of the from "re:*")
-    -  a negated regexp (can be given by a string of the form "!re:*")
-
-    If called with no arguments, then the list of builtins is returned, mapped
-    to their meaning.
-
-    Otherwise the function returns the smallest FUSE API version number which
-    has all the matching features.
-
-    Builtin specifiers worth to explicit mention:
-    - ``stateful_files``: you want to use custom filehandles (eg. a file class).
-    - ``*``: you want all features.
-    - while ``has_foo`` makes sense for all filesystem method ``foo``, some
-      of these can be found among the builtins, too (the ones which can be
-      handled by the general rule).
-
-    specifiers like ``has_foo`` refer to requirement that the library knows of
-      the fs method ``foo``.
-    """
-
-    fmap = {'stateful_files': 22,
-            'stateful_dirs':  23,
-            'stateful_io':    ('stateful_files', 'stateful_dirs'),
-            'stateful_files_keep_cache': 23,
-            'stateful_files_direct_io': 23,
-            'keep_cache':     ('stateful_files_keep_cache',),
-            'direct_io':      ('stateful_files_direct_io',),
-            'has_opendir':    ('stateful_dirs',),
-            'has_releasedir': ('stateful_dirs',),
-            'has_fsyncdir':   ('stateful_dirs',),
-            'has_create':     25,
-            'has_access':     25,
-            'has_fgetattr':   25,
-            'has_ftruncate':  25,
-            'has_fsinit':     ('has_init'),
-            'has_fsdestroy':  ('has_destroy'),
-            'has_lock':       26,
-            'has_utimens':    26,
-            'has_bmap':       26,
-            'has_init':       23,
-            'has_destroy':    23,
-            '*':              '!re:^\*$'}
-
-    if not feas:
-        return fmap
-
-    def resolve(args, maxva):
-
-        for fp in args:
-            if isinstance(fp, int):
-                maxva[0] = max(maxva[0], fp)
-                continue
-            if isinstance(fp, list) or isinstance(fp, tuple):
-                for f in fp:
-                    yield f
-                continue
-            ma = isinstance(fp, str) and re.compile("(!\s*|)re:(.*)").match(fp)
-            if isinstance(fp, type(re.compile(''))) or ma:
-                neg = False
-                if ma:
-                    mag = ma.groups()
-                    fp = re.compile(mag[1])
-                    neg = bool(mag[0])
-                for f in fmap.keys() + [ 'has_' + a for a in Fuse._attrs ]:
-                    if neg != bool(re.search(fp, f)):
-                        yield f
-                continue
-            ma = re.compile("has_(.*)").match(fp)
-            if ma and ma.groups()[0] in Fuse._attrs and not fp in fmap:
-                yield 21
-                continue
-            yield fmap[fp]
-
-    maxva = [0]
-    while feas:
-        feas = set(resolve(feas, maxva))
-
-    return maxva[0]
-
-
-def APIVersion():
-    """Get the API version of your underlying FUSE lib"""
-
-    return FuseAPIVersion()
-
-
-def feature_assert(*feas):
-    """
-    Takes some feature patterns (like in `feature_needs`).
-    Raises a fuse.FuseError if your underlying FUSE lib fails
-    to have some of the matching features.
-
-    (Note: use a ``has_foo`` type feature assertion only if lib support
-    for method ``foo`` is *necessary* for your fs. Don't use this assertion
-    just because your fs implements ``foo``. The usefulness of ``has_foo``
-    is limited by the fact that we can't guarantee that your FUSE kernel
-    module also supports ``foo``.)
-    """
-
-    fav = APIVersion()
-
-    for fea in feas:
-        fn = feature_needs(fea)
-        if fav < fn:
-            raise FuseError(
-              "FUSE API version %d is required for feature `%s' but only %d is available" % \
-              (fn, str(fea), fav))
-
-
-############# Subclass this.
-
-class Fuse(object):
-    """
-    Python interface to FUSE.
-
-    Basic usage:
-
-    - instantiate
-
-    - add options to `parser` attribute (an instance of `FuseOptParse`)
-
-    - call `parse`
-
-    - call `main`
-    """
-
-    _attrs = ['getattr', 'readlink', 'readdir', 'mknod', 'mkdir',
-              'unlink', 'rmdir', 'symlink', 'rename', 'link', 'chmod',
-              'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
-              'statfs', 'fsync', 'create', 'opendir', 'releasedir', 'fsyncdir',
-              'flush', 'fgetattr', 'ftruncate', 'getxattr', 'listxattr',
-              'setxattr', 'removexattr', 'access', 'lock', 'utimens', 'bmap',
-              'fsinit', 'fsdestroy']
-
-    fusage = "%prog [mountpoint] [options]"
-
-    def __init__(self, *args, **kw):
-        """
-        Not much happens here apart from initializing the `parser` attribute.
-        Arguments are forwarded to the constructor of the parser class almost
-        unchanged.
-
-        The parser class is `FuseOptParse` unless you specify one using the
-        ``parser_class`` keyword. (See `FuseOptParse` documentation for
-        available options.)
-        """
-
-        if not fuse_python_api:
-            raise RuntimeError, __name__ + """.fuse_python_api not defined.
-
-! Please define """ + __name__ + """.fuse_python_api internally (eg.
-! 
-! (1)  """ + __name__ + """.fuse_python_api = """ + `FUSE_PYTHON_API_VERSION` + """
-! 
-! ) or in the enviroment (eg. 
-! 
-! (2)  FUSE_PYTHON_API=0.1
-! 
-! ).
-!
-! If you are actually developing a filesystem, probably (1) is the way to go.
-! If you are using a filesystem written before 2007 Q2, probably (2) is what
-! you want."
-"""
-
-        def malformed():
-            raise RuntimeError, \
-                  "malformatted fuse_python_api value " + `fuse_python_api`
-        if not isinstance(fuse_python_api, tuple):
-            malformed()
-        for i in fuse_python_api:
-            if not isinstance(i, int) or i < 0:
-                malformed() 
-
-        if fuse_python_api > FUSE_PYTHON_API_VERSION:
-            raise RuntimeError, """
-! You require FUSE-Python API version """ + `fuse_python_api` + """.
-! However, the latest available is """ + `FUSE_PYTHON_API_VERSION` + """.
-"""
-
-        self.fuse_args = \
-            'fuse_args' in kw and kw.pop('fuse_args') or FuseArgs()
-
-        if get_compat_0_1():
-            return self.__init_0_1__(*args, **kw)
-
-        self.multithreaded = True
-
-        if not 'usage' in kw:
-            kw['usage'] = self.fusage
-        if not 'fuse_args' in kw:
-            kw['fuse_args'] = self.fuse_args
-        kw['fuse'] = self
-        parserclass = \
-          'parser_class' in kw and kw.pop('parser_class') or FuseOptParse
-
-        self.parser = parserclass(*args, **kw)
-        self.methproxy = self.Methproxy()
-
-    def parse(self, *args, **kw):
-        """Parse command line, fill `fuse_args` attribute."""
-
-        ev = 'errex' in kw and kw.pop('errex')
-        if ev and not isinstance(ev, int):
-            raise TypeError, "error exit value should be an integer"
-
-        try:
-            self.cmdline = self.parser.parse_args(*args, **kw)
-        except OptParseError:
-          if ev:
-              sys.exit(ev)
-          raise
-
-        return self.fuse_args
-
-    def main(self, args=None):
-        """Enter filesystem service loop."""
-
-        if get_compat_0_1():
-            args = self.main_0_1_preamble()
-
-        d = {'multithreaded': self.multithreaded and 1 or 0}
-        d['fuse_args'] = args or self.fuse_args.assemble()
-
-        for t in 'file_class', 'dir_class':
-            if hasattr(self, t):
-                getattr(self.methproxy, 'set_' + t)(getattr(self,t))
-
-        for a in self._attrs:
-            b = a
-            if get_compat_0_1() and a in self.compatmap:
-                b = self.compatmap[a]
-            if hasattr(self, b):
-                c = ''
-                if get_compat_0_1() and hasattr(self, a + '_compat_0_1'):
-                    c = '_compat_0_1'
-                d[a] = ErrnoWrapper(self.lowwrap(a + c))
-
-        try:
-            main(**d)
-        except FuseError:
-            if args or self.fuse_args.mount_expected():
-                raise
-
-    def lowwrap(self, fname):
-        """
-        Wraps the fname method when the C code expects a different kind of
-        callback than we have in the fusepy API. (The wrapper is usually for
-        performing some checks or transfromations which could be done in C but
-        is simpler if done in Python.)
-
-        Currently `open` and `create` are wrapped: a boolean flag is added
-        which indicates if the result is to be kept during the opened file's
-        lifetime or can be thrown away. Namely, it's considered disposable
-        if it's an instance of FuseFileInfo.
-        """
-        fun = getattr(self, fname)
-
-        if fname in ('open', 'create'):
-            def wrap(*a, **kw):
-                res = fun(*a, **kw)
-                if not res or type(res) == type(0):
-                    return res
-                else:
-                    return (res, type(res) != FuseFileInfo)
-        elif fname == 'utimens':
-            def wrap(path, acc_sec, acc_nsec, mod_sec, mod_nsec):
-                ts_acc = Timespec(tv_sec = acc_sec, tv_nsec = acc_nsec)
-                ts_mod = Timespec(tv_sec = mod_sec, tv_nsec = mod_nsec)
-                return fun(path, ts_acc, ts_mod)
-        else:
-            wrap = fun
-
-        return wrap
-
-    def GetContext(self):
-        return FuseGetContext(self)
-
-    def Invalidate(self, path):
-        return FuseInvalidate(self, path)
-
-    def fuseoptref(cls):
-        """
-        Find out which options are recognized by the library.
-        Result is a `FuseArgs` instance with the list of supported
-        options, suitable for passing on to the `filter` method of
-        another `FuseArgs` instance.
-        """
-
-        import os, re
-
-        pr, pw = os.pipe()
-        pid = os.fork()
-        if pid == 0:
-             os.dup2(pw, 2)
-             os.close(pr)
-
-             fh = cls()
-             fh.fuse_args = FuseArgs()
-             fh.fuse_args.setmod('showhelp')
-             fh.main()
-             sys.exit()
-
-        os.close(pw)
-
-        fa = FuseArgs()
-        ore = re.compile("-o\s+([\w\[\]]+(?:=\w+)?)")
-        fpr = os.fdopen(pr)
-        for l in fpr:
-             m = ore.search(l)
-             if m:
-                 o = m.groups()[0]
-                 oa = [o]
-                 # try to catch two-in-one options (like "[no]foo")
-                 opa = o.split("[")
-                 if len(opa) == 2:
-                    o1, ox = opa
-                    oxpa = ox.split("]")
-                    if len(oxpa) == 2:
-                       oo, o2 = oxpa
-                       oa = [o1 + o2, o1 + oo + o2]
-                 for o in oa:
-                     fa.add(o)
-
-        fpr.close()
-        return fa
-
-    fuseoptref = classmethod(fuseoptref)
-
-
-    class Methproxy(object):
-
-        def __init__(self):
-
-            class mpx(object):
-               def __init__(self, name):
-                   self.name = name
-               def __call__(self, *a, **kw):
-                   return getattr(a[-1], self.name)(*(a[1:-1]), **kw)
-
-            self.proxyclass = mpx
-            self.mdic = {}
-            self.file_class = None
-            self.dir_class = None
-
-        def __call__(self, meth):
-            return meth in self.mdic and self.mdic[meth] or None
-
-        def _add_class_type(cls, type, inits, proxied):
-
-            def setter(self, xcls):
-
-                setattr(self, type + '_class', xcls)
-
-                for m in inits:
-                    self.mdic[m] = xcls
-
-                for m in proxied:
-                    if hasattr(xcls, m):
-                        self.mdic[m] = self.proxyclass(m)
-
-            setattr(cls, 'set_' + type + '_class', setter)
-
-        _add_class_type = classmethod(_add_class_type)
-
-    Methproxy._add_class_type('file', ('open', 'create'),
-                              ('read', 'write', 'fsync', 'release', 'flush',
-                               'fgetattr', 'ftruncate', 'lock'))
-    Methproxy._add_class_type('dir', ('opendir',),
-                              ('readdir', 'fsyncdir', 'releasedir'))
-
-
-    def __getattr__(self, meth):
-
-        m = self.methproxy(meth)
-        if m:
-            return m
-
-        raise AttributeError, "Fuse instance has no attribute '%s'" % meth
-
-
-
-##########
-###
-###  Compat stuff.
-###
-##########
-
-
-
-    def __init_0_1__(self, *args, **kw):
-
-        self.flags = 0
-        multithreaded = 0
-
-        # default attributes
-        if args == ():
-            # there is a self.optlist.append() later on, make sure it won't
-            # bomb out.
-            self.optlist = []
-        else:
-            self.optlist = args
-        self.optdict = kw
-
-        if len(self.optlist) == 1:
-            self.mountpoint = self.optlist[0]
-        else:
-            self.mountpoint = None
-
-        # grab command-line arguments, if any.
-        # Those will override whatever parameters
-        # were passed to __init__ directly.
-        argv = sys.argv
-        argc = len(argv)
-        if argc > 1:
-            # we've been given the mountpoint
-            self.mountpoint = argv[1]
-        if argc > 2:
-            # we've received mount args
-            optstr = argv[2]
-            opts = optstr.split(",")
-            for o in opts:
-                try:
-                    k, v = o.split("=", 1)
-                    self.optdict[k] = v
-                except:
-                    self.optlist.append(o)
-
-
-    def main_0_1_preamble(self):
-
-        cfargs = FuseArgs()
-
-        cfargs.mountpoint = self.mountpoint
-
-        if hasattr(self, 'debug'):
-            cfargs.add('debug')
-
-        if hasattr(self, 'allow_other'):
-            cfargs.add('allow_other')
-
-        if hasattr(self, 'kernel_cache'):
-            cfargs.add('kernel_cache')
-
-        return cfargs.assemble()
-
-
-    def getattr_compat_0_1(self, *a):
-        from os import stat_result
-
-        return stat_result(self.getattr(*a))
-
-
-    def statfs_compat_0_1(self, *a):
-
-        oout = self.statfs(*a)
-        lo = len(oout)
-
-        svf = StatVfs()
-        svf.f_bsize   = oout[0]                   # 0
-        svf.f_frsize  = oout[lo >= 8 and 7 or 0]  # 1
-        svf.f_blocks  = oout[1]                   # 2
-        svf.f_bfree   = oout[2]                   # 3
-        svf.f_bavail  = oout[3]                   # 4
-        svf.f_files   = oout[4]                   # 5
-        svf.f_ffree   = oout[5]                   # 6
-        svf.f_favail  = lo >= 9 and oout[8] or 0  # 7
-        svf.f_flag    = lo >= 10 and oout[9] or 0 # 8
-        svf.f_namemax = oout[6]                   # 9
-
-        return svf
-
-
-    def readdir_compat_0_1(self, path, offset, *fh):
-
-        for name, type in self.getdir(path):
-            de = Direntry(name)
-            de.type = type
-
-            yield de
-
-
-    compatmap = {'readdir': 'getdir'}
diff --git a/mac/macfuse/fuseparts/__init__.py b/mac/macfuse/fuseparts/__init__.py
deleted file mode 100644 (file)
index 0988857..0000000
+++ /dev/null
@@ -1 +0,0 @@
-__version__ = "0.2"
diff --git a/mac/macfuse/fuseparts/_fusemodule.so b/mac/macfuse/fuseparts/_fusemodule.so
deleted file mode 100644 (file)
index c802be4..0000000
Binary files a/mac/macfuse/fuseparts/_fusemodule.so and /dev/null differ
diff --git a/mac/macfuse/fuseparts/setcompatwrap.py b/mac/macfuse/fuseparts/setcompatwrap.py
deleted file mode 100644 (file)
index 7ead738..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-try:
-  set()
-  set = set
-except:
-  from sets import Set as set
diff --git a/mac/macfuse/fuseparts/subbedopts.py b/mac/macfuse/fuseparts/subbedopts.py
deleted file mode 100644 (file)
index 9cf40ae..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-#
-#    Copyright (C) 2006  Csaba Henk  <csaba.henk@creo.hu>
-#
-#    This program can be distributed under the terms of the GNU LGPL.
-#    See the file COPYING.
-#
-
-from optparse import Option, OptionParser, OptParseError, OptionConflictError
-from optparse import HelpFormatter, IndentedHelpFormatter, SUPPRESS_HELP
-from fuseparts.setcompatwrap import set
-
-##########
-###
-###  Generic suboption parsing stuff.
-###
-##########
-
-
-
-class SubOptsHive(object):
-    """
-    Class for collecting unhandled suboptions.
-    """
-
-    def __init__(self):
-
-        self.optlist = set()
-        self.optdict = {}
-
-    def _str_core(self):
-
-        sa = []
-        for k, v in self.optdict.iteritems():
-             sa.append(str(k) + '=' + str(v))
-
-        ra = (list(self.optlist) + sa) or ["(none)"]
-        ra.sort()
-        return ra 
-
-    def __str__(self):
-        return "< opts: " + ", ".join(self._str_core()) + " >"
-
-    def canonify(self):
-        """
-        Transform self to an equivalent canonical form:
-        delete optdict keys with False value, move optdict keys
-        with True value to optlist, stringify other values.
-        """
-
-        for k, v in self.optdict.iteritems():
-            if v == False:
-                self.optdict.pop(k)
-            elif v == True:
-                self.optdict.pop(k)
-                self.optlist.add(v)
-            else:
-                self.optdict[k] = str(v)
-
-    def filter(self, other):
-        """
-        Throw away those options which are not in the other one.
-        Returns a new instance with the rejected options.
-        """
-
-        self.canonify()
-        other.canonify()
-
-        rej = self.__class__()
-        rej.optlist = self.optlist.difference(other.optlist)
-        self.optlist.difference_update(rej.optlist)
-        for x in self.optdict.copy():
-             if x not in other.optdict:
-                 self.optdict.pop(x)
-                 rej.optdict[x] = None
-
-        return rej
-
-    def add(self, opt, val=None):
-        """Add a suboption."""
-
-        ov = opt.split('=', 1)
-        o = ov[0]
-        v = len(ov) > 1 and ov[1] or None
-
-        if (v):
-            if val != None:
-                raise AttributeError, "ambiguous option value"
-            val = v
-
-        if val == False:
-            return
-
-        if val in (None, True):
-            self.optlist.add(o)
-        else:
-            self.optdict[o] = val
-
-
-
-class SubbedOpt(Option):
-    """
-    `Option` derivative enhanced with the attribute of being a suboption of
-     some other option (like ``foo`` and ``bar`` for ``-o`` in ``-o foo,bar``).
-    """
-
-    ATTRS = Option.ATTRS + ["subopt", "subsep", "subopts_hive"]
-    ACTIONS = Option.ACTIONS + ("store_hive",)
-    STORE_ACTIONS = Option.STORE_ACTIONS + ("store_hive",)
-    TYPED_ACTIONS = Option.TYPED_ACTIONS + ("store_hive",)
-
-    def __init__(self, *opts, **attrs):
-
-       self.subopt_map = {}
-
-       if "subopt" in attrs:
-           self._short_opts = []
-           self._long_opts = []
-           self._set_opt_strings(opts)
-           self.baseopt = self._short_opts[0] or self._long_opts[0]
-           opts = ()
-
-       Option.__init__(self, *opts, **attrs)
-
-    def __str__(self):
-        pf = ""
-        if hasattr(self, "subopt") and self.subopt:
-            pf = " %s...,%s,..." % (self.baseopt, self.subopt)
-        return Option.__str__(self) + pf
-
-    def _check_opt_strings(self, opts):
-        return opts
-
-    def _check_dest(self):
-        try:
-            Option._check_dest(self)
-        except IndexError:
-            if self.subopt:
-                self.dest = "__%s__%s" % (self.baseopt, self.subopt)
-                self.dest = self.dest.replace("-", "")
-            else:
-                raise
-
-    def get_opt_string(self):
-        if hasattr(self, 'subopt'):
-            return self.subopt
-        else:
-            return Option.get_opt_string(self)
-
-    def take_action(self, action, dest, opt, value, values, parser):
-        if action == "store_hive":
-            if not hasattr(values, dest) or getattr(values, dest) == None:
-                 if hasattr(self, "subopts_hive") and self.subopts_hive:
-                     hive = self.subopts_hive
-                 else:
-                     hive = parser.hive_class()
-                 setattr(values, dest, hive)
-            for o in value.split(self.subsep or ","):
-                oo = o.split('=')
-                ok = oo[0]
-                ov = None
-                if (len(oo) > 1):
-                    ov = oo[1]
-                if ok in self.subopt_map:
-                    self.subopt_map[ok].process(ok, ov, values, parser)
-                else:
-                    getattr(values, dest).add(*oo)
-            return
-        Option.take_action(self, action, dest, opt, value, values, parser)
-
-    def register_sub(self, o):
-        """Register argument a suboption for `self`."""
-
-        if o.subopt in self.subopt_map:
-            raise OptionConflictError(
-              "conflicting suboption handlers for `%s'" % o.subopt,
-              o)
-        self.subopt_map[o.subopt] = o
-
-    CHECK_METHODS = []
-    for m in Option.CHECK_METHODS:
-        #if not m == Option._check_dest:
-        if not m.__name__ == '_check_dest':
-            CHECK_METHODS.append(m)
-    CHECK_METHODS.append(_check_dest)
-
-
-
-class SubbedOptFormatter(HelpFormatter):
-
-    def format_option_strings(self, option):
-        if hasattr(option, "subopt") and option.subopt:
-            res = '-o ' + option.subopt
-            if option.takes_value():
-                res += "="
-                res += option.metavar or 'FOO'
-            return res
-
-        return HelpFormatter.format_option_strings(self, option)
-
-
-
-class SubbedOptIndentedFormatter(IndentedHelpFormatter, SubbedOptFormatter):
-
-    def format_option_strings(self, option):
-        return SubbedOptFormatter.format_option_strings(self, option)
-
-
-
-class SubbedOptParse(OptionParser):
-    """
-    This class alters / enhances `OptionParser` with *suboption handlers*.
-
-    That is, calling `sop.add_option('-x', subopt=foo)` installs a handler
-    which will be triggered if there is ``-x foo`` in the command line being
-    parsed (or, eg., ``-x foo,bar``).
-
-    Moreover, ``-x`` implicitly gets a handler which collects the unhandled
-    suboptions of ``-x`` into a `SubOptsHive` instance (accessible post festam
-    via the `x` attribute of the returned Values object). (The only exception
-    is when ``-x`` has *explicitly*  been added with action ``store_hive``.
-    This opens up the possibility of customizing the ``-x`` handler at some
-    rate.)
-
-    Suboption handlers have all the nice features of normal option handlers,
-    eg. they are displayed in the automatically generated help message
-    (and can have their own help info).
-    """
-
-    def __init__(self, *args, **kw):
-
-         if not 'formatter' in kw:
-             kw['formatter'] = SubbedOptIndentedFormatter()
-         if not 'option_class' in kw:
-             kw['option_class'] = SubbedOpt
-         if 'hive_class' in kw:
-             self.hive_class = kw.pop('hive_class')
-         else:
-             self.hive_class = SubOptsHive
-
-         OptionParser.__init__(self, *args, **kw)
-
-    def add_option(self, *args, **kwargs):
-        if 'action' in kwargs and kwargs['action'] == 'store_hive':
-            if 'subopt' in kwargs:
-                raise OptParseError(
-                  """option can't have a `subopt' attr and `action="store_hive"' at the same time""")
-            if not 'type' in kwargs:
-                kwargs['type'] = 'string'
-        elif 'subopt' in kwargs:
-            o = self.option_class(*args, **kwargs)
-
-            oo = self.get_option(o.baseopt)
-            if oo:
-                if oo.action != "store_hive":
-                    raise OptionConflictError(
-                      "can't add subopt as option has already a handler that doesn't do `store_hive'",
-                      oo)
-            else:
-                self.add_option(o.baseopt, action='store_hive',
-                                metavar="sub1,[sub2,...]")
-                oo = self.get_option(o.baseopt)
-
-            oo.register_sub(o)
-
-            args = (o,)
-            kwargs = {}
-
-        return OptionParser.add_option(self, *args, **kwargs)
diff --git a/mac/macfuse/tahoefuse.py b/mac/macfuse/tahoefuse.py
deleted file mode 100644 (file)
index 470898d..0000000
+++ /dev/null
@@ -1,713 +0,0 @@
-#!/usr/bin/env python
-
-#-----------------------------------------------------------------------------------------------
-from allmydata.uri import CHKFileURI, NewDirectoryURI, LiteralFileURI
-from allmydata.scripts.common_http import do_http as do_http_req
-
-import base64
-import sha
-import sys
-import os
-import errno
-import stat
-# pull in some spaghetti to make this stuff work without fuse-py being installed
-try:
-    import _find_fuse_parts
-    junk = _find_fuse_parts
-    del junk
-except ImportError:
-    pass
-import fuse
-
-import time
-import traceback
-import simplejson
-import urllib
-
-USAGE = 'usage: tahoe fuse [dir_cap_name] [fuse_options] mountpoint'
-
-if not hasattr(fuse, '__version__'):
-    raise RuntimeError, \
-        "your fuse-py doesn't know of fuse.__version__, probably it's too old."
-
-fuse.fuse_python_api = (0, 2)
-fuse.feature_assert('stateful_files', 'has_init')
-
-
-logfile = file('tfuse.log', 'wb')
-
-def log(msg):
-    logfile.write("%s: %s\n" % (time.asctime(), msg))
-    #time.sleep(0.1)
-    logfile.flush()
-
-fuse.flog = log
-
-def do_http(method, url, body=''):
-    resp = do_http_req(method, url, body)
-    log('do_http(%s, %s) -> %s, %s' % (method, url, resp.status, resp.reason))
-    if resp.status not in (200, 201):
-        raise RuntimeError('http response (%s, %s)' % (resp.status, resp.reason))
-    else:
-        return resp.read()
-
-def flag2mode(flags):
-    log('flag2mode(%r)' % (flags,))
-    #md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
-    md = {os.O_RDONLY: 'rb', os.O_WRONLY: 'wb', os.O_RDWR: 'w+b'}
-    m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
-
-    if flags | os.O_APPEND:
-        m = m.replace('w', 'a', 1)
-
-    return m
-
-def logargsretexc(meth):
-    def inner(self, *args, **kwargs):
-        log("%s(%r, %r)" % (meth, args, kwargs))
-        try:
-            ret = meth(self, *args, **kwargs)
-        except:
-            log('exception:\n%s' % (traceback.format_exc(),))
-            raise
-        log("ret: %r" % (ret, ))
-        return ret
-    inner.__name__ = '<logwrap(%s)>' % (meth,)
-    return inner
-
-def logexc(meth):
-    def inner(self, *args, **kwargs):
-        try:
-            ret = meth(self, *args, **kwargs)
-        except:
-            log('exception:\n%s' % (traceback.format_exc(),))
-            raise
-        return ret
-    inner.__name__ = '<logwrap(%s)>' % (meth,)
-    return inner
-
-def log_exc():
-    log('exception:\n%s' % (traceback.format_exc(),))
-
-class TahoeFuseFile(object):
-
-    def __init__(self, path, flags, *mode):
-        log("TFF: __init__(%r, %r, %r)" % (path, flags, mode))
-
-        self._path = path # for tahoe put
-        try:
-            self.parent, self.name, self.fnode = self.tfs.get_parent_name_and_child(path)
-            m = flag2mode(flags)
-            log('TFF: flags2(mode) -> %s' % (m,))
-            if m[0] in 'wa':
-                # write
-                self.fname = self.tfs.cache.tmp_file(os.urandom(20))
-                if self.fnode is None:
-                    log('TFF: [%s] open(%s) for write: no such file, creating new File' % (self.name, self.fname, ))
-                    self.fnode = File(0, None, None)
-                    self.fnode.tmp_fname = self.fname # XXX kill this
-                    self.parent.add_child(self.name, self.fnode)
-                elif hasattr(self.fnode, 'tmp_fname'):
-                    self.fname = self.fnode.tmp_fname
-                self.file = os.fdopen(os.open(self.fname, flags, *mode), m)
-                self.fd = self.file.fileno()
-                log('TFF: opened(%s) for write' % self.fname)
-                self.open_for_write = True
-            else:
-                # read
-                assert self.fnode is not None
-                uri = self.fnode.get_uri()
-
-                # XXX make this go away
-                if hasattr(self.fnode, 'tmp_fname'):
-                    self.fname = self.fnode.tmp_fname
-                    log('TFF: reopening(%s) for reading' % self.fname)
-                else:
-                    log('TFF: fetching file from cache for reading')
-                    self.fname = self.tfs.cache.get_file(uri)
-
-                self.file = os.fdopen(os.open(self.fname, flags, *mode), m)
-                self.fd = self.file.fileno()
-                self.open_for_write = False
-                log('TFF: opened(%s) for read' % self.fname)
-        except:
-            log_exc()
-            raise
-
-    def log(self, msg):
-        log("<TFF(%s:%s)> %s" % (os.path.basename(self.fname), self.name, msg))
-
-    @logexc
-    def read(self, size, offset):
-        self.log('read(%r, %r)' % (size, offset, ))
-        self.file.seek(offset)
-        return self.file.read(size)
-
-    @logexc
-    def write(self, buf, offset):
-        self.log("write(-%s-, %r)" % (len(buf), offset))
-        if not self.open_for_write:
-            return -errno.EACCES
-        self.file.seek(offset)
-        self.file.write(buf)
-        return len(buf)
-
-    @logexc
-    def release(self, flags):
-        self.log("release(%r)" % (flags,))
-        self.file.close()
-        if self.open_for_write:
-            size = os.path.getsize(self.fname)
-            self.fnode.size = size
-            file_cap = self.tfs.upload(self.fname)
-            self.fnode.ro_uri = file_cap
-            self.tfs.add_child(self.parent.get_uri(), self.name, file_cap)
-            self.log("uploaded: %s" % (file_cap,))
-
-        # dbg
-        print_tree()
-
-    def _fflush(self):
-        if 'w' in self.file.mode or 'a' in self.file.mode:
-            self.file.flush()
-
-    @logexc
-    def fsync(self, isfsyncfile):
-        self.log("fsync(%r)" % (isfsyncfile,))
-        self._fflush()
-        if isfsyncfile and hasattr(os, 'fdatasync'):
-            os.fdatasync(self.fd)
-        else:
-            os.fsync(self.fd)
-
-    @logexc
-    def flush(self):
-        self.log("flush()")
-        self._fflush()
-        # cf. xmp_flush() in fusexmp_fh.c
-        os.close(os.dup(self.fd))
-
-    @logexc
-    def fgetattr(self):
-        self.log("fgetattr()")
-        s = os.fstat(self.fd)
-        self.log("fgetattr() -> %r" % (s,))
-        return s
-
-    @logexc
-    def ftruncate(self, len):
-        self.log("ftruncate(%r)" % (len,))
-        self.file.truncate(len)
-
-class ObjFetcher(object):
-    def get_tahoe_file(self, path, flags, *mode):
-        log('objfetcher.get_tahoe_file(%r, %r, %r, %r)' % (self, path, flags, mode))
-        return TahoeFuseFile(path, flags, *mode)
-fetcher = ObjFetcher()
-
-class TahoeFuse(fuse.Fuse):
-
-    def __init__(self, tfs, *args, **kw):
-        log("TF: __init__(%r, %r)" % (args, kw))
-
-        self.tfs = tfs
-        _tfs_ = tfs
-        class MyFuseFile(TahoeFuseFile):
-            tfs = _tfs_
-        self.file_class = MyFuseFile
-        log("TF: file_class: %r" % (self.file_class,))
-
-        fuse.Fuse.__init__(self, *args, **kw)
-
-        #import thread
-        #thread.start_new_thread(self.launch_reactor, ())
-
-    def log(self, msg):
-        log("<TF> %s" % (msg, ))
-
-    @logexc
-    def readlink(self, path):
-        self.log("readlink(%r)" % (path,))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def unlink(self, path):
-        self.log("unlink(%r)" % (path,))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def rmdir(self, path):
-        self.log("rmdir(%r)" % (path,))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def symlink(self, path, path1):
-        self.log("symlink(%r, %r)" % (path, path1))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def rename(self, path, path1):
-        self.log("rename(%r, %r)" % (path, path1))
-        self.tfs.rename(path, path1)
-
-    @logexc
-    def link(self, path, path1):
-        self.log("link(%r, %r)" % (path, path1))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def chmod(self, path, mode):
-        self.log("chmod(%r, %r)" % (path, mode))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def chown(self, path, user, group):
-        self.log("chown(%r, %r, %r)" % (path, user, group))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def truncate(self, path, len):
-        self.log("truncate(%r, %r)" % (path, len))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def utime(self, path, times):
-        self.log("utime(%r, %r)" % (path, times))
-        return -errno.EOPNOTSUPP
-
-    @logexc
-    def statfs(self):
-        self.log("statfs()")
-        """
-        Should return an object with statvfs attributes (f_bsize, f_frsize...).
-        Eg., the return value of os.statvfs() is such a thing (since py 2.2).
-        If you are not reusing an existing statvfs object, start with
-        fuse.StatVFS(), and define the attributes.
-
-        To provide usable information (ie., you want sensible df(1)
-        output, you are suggested to specify the following attributes:
-
-            - f_bsize - preferred size of file blocks, in bytes
-            - f_frsize - fundamental size of file blcoks, in bytes
-                [if you have no idea, use the same as blocksize]
-            - f_blocks - total number of blocks in the filesystem
-            - f_bfree - number of free blocks
-            - f_files - total number of file inodes
-            - f_ffree - nunber of free file inodes
-        """
-
-        return os.statvfs(".")
-
-    def fsinit(self):
-        self.log("fsinit()")
-
-    def main(self, *a, **kw):
-        self.log("main(%r, %r)" % (a, kw))
-
-        return fuse.Fuse.main(self, *a, **kw)
-
-    ##################################################################
-
-    @logexc
-    def readdir(self, path, offset):
-        log('readdir(%r, %r)' % (path, offset))
-        node = self.tfs.get_path(path)
-        if node is None:
-            return -errno.ENOENT
-        dirlist = ['.', '..'] + node.children.keys()
-        log('dirlist = %r' % (dirlist,))
-        return [fuse.Direntry(d) for d in dirlist]
-
-    @logexc
-    def getattr(self, path):
-        log('getattr(%r)' % (path,))
-        node = self.tfs.get_path(path)
-        if node is None:
-            return -errno.ENOENT
-        return node.get_stat()
-
-    @logexc
-    def access(self, path, mode):
-        self.log("access(%r, %r)" % (path, mode))
-        node = self.tfs.get_path(path)
-        if not node:
-            return -errno.ENOENT
-        accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
-        if (mode & 0222):
-            if not node.writable():
-                log('write access denied for %s (req:%o)' % (path, mode, ))
-                return -errno.EACCES
-        #else:
-            #log('access granted for %s' % (path, ))
-
-    @logexc
-    def mkdir(self, path, mode):
-        self.log("mkdir(%r, %r)" % (path, mode))
-        self.tfs.mkdir(path)
-
-def launch_tahoe_fuse(tfs, argv):
-    sys.argv = ['tahoe fuse'] + list(argv)
-    server = TahoeFuse(tfs, version="%prog " + fuse.__version__,
-                       usage=USAGE,
-                       dash_s_do='setsingle')
-    server.parse(errex=1)
-    server.main()
-
-
-def getbasedir(cap_name='root_dir'):
-    fname = os.path.expanduser("~/.tahoe/private/%s.cap" % (cap_name,))
-    if os.path.exists(fname):
-        f = file(fname, 'rb')
-        bd = f.read().strip()
-        f.close()
-        return bd
-    else:
-        return None
-
-def getnodeurl():
-    f = file(os.path.expanduser("~/.tahoe/node.url"), 'rb')
-    nu = f.read().strip()
-    f.close()
-    if nu[-1] != "/":
-        nu += "/"
-    return nu
-
-def fingerprint(uri):
-    if uri is None:
-        return None
-    return base64.b32encode(sha.new(uri).digest()).lower()[:6]
-
-class TStat(fuse.Stat):
-    def __init__(self, **kwargs):
-        fuse.Stat.__init__(self, **kwargs)
-
-    def __repr__(self):
-        return "<Stat%r" % {
-            'st_mode': self.st_mode,
-            'st_ino': self.st_ino,
-            'st_dev': self.st_dev,
-            'st_nlink': self.st_nlink,
-            'st_uid': self.st_uid,
-            'st_gid': self.st_gid,
-            'st_size': self.st_size,
-            'st_atime': self.st_atime,
-            'st_mtime': self.st_mtime,
-            'st_ctime': self.st_ctime,
-            }
-
-class Directory(object):
-    def __init__(self, ro_uri, rw_uri):
-        self.ro_uri = ro_uri
-        self.rw_uri = rw_uri
-        assert (rw_uri or ro_uri)
-        self.children = {}
-
-    def __repr__(self):
-        return "<Directory %s>" % (fingerprint(self.get_uri()),)
-
-    def get_children(self):
-        return self.children.keys()
-
-    def get_child(self, name):
-        return self.children[name]
-
-    def add_child(self, name, file_node):
-        self.children[name] = file_node
-
-    def remove_child(self, name):
-        del self.children[name]
-
-    def get_uri(self):
-        return self.rw_uri or self.ro_uri
-
-    def writable(self):
-        return self.rw_uri and self.rw_uri != self.ro_uri
-
-    def pprint(self, prefix='', printed=None):
-        ret = []
-        if printed is None:
-            printed = set()
-        writable = self.writable() and '+' or ' '
-        if self in printed:
-            ret.append("         %s/%s ... <%s>" % (prefix, writable, fingerprint(self.get_uri()), ))
-        else:
-            ret.append("[%s] %s/%s" % (fingerprint(self.get_uri()), prefix, writable, ))
-            printed.add(self)
-            for name,f in sorted(self.children.items()):
-                ret.append(f.pprint(' ' * (len(prefix)+1)+name, printed))
-        return '\n'.join(ret)
-
-    def get_stat(self):
-        s = TStat(st_mode = stat.S_IFDIR | 0755, st_nlink = 2)
-        log("%s.get_stat()->%s" % (self, s))
-        return s
-
-class File(object):
-    def __init__(self, size, ro_uri, metadata):
-        self.size = size
-        if ro_uri:
-            ro_uri = str(ro_uri)
-        self.ro_uri = ro_uri
-        self.metadata = metadata or {}
-
-    def __repr__(self):
-        return "<File %s>" % (fingerprint(self.ro_uri) or [self.tmp_fname],)
-
-    def pprint(self, prefix='', printed=None):
-        times, remainder = self.get_times()
-        return "         %s (%s) %s %s" % (prefix, self.size, times, remainder)
-
-    def get_times(self):
-        rem = self.metadata.copy()
-        now = time.time()
-        times = {}
-        for T in ['c', 'm']:
-            t = rem.pop('%stime'%T, None)
-            if not t:
-                t = 'none'
-            elif (now-t) < 86400:
-                t = time.strftime('%a:%H:%M:%S', time.localtime(t))
-            else:
-                t = time.strftime('%Y:%b:%d:%H:%M', time.localtime(t))
-            times[T] = t
-        return times, rem
-
-    def get_stat(self):
-        if hasattr(self, 'tmp_fname'):
-            s = os.stat(self.tmp_fname)
-            log("%s.get_stat()->%s" % (self, s))
-        else:
-            s = TStat(st_size=self.size, st_mode = stat.S_IFREG | 0444, st_nlink = 1)
-            log("%s.get_stat()->%s" % (self, s))
-        return s
-
-    def get_uri(self):
-        return self.ro_uri
-
-    def writable(self):
-        #return not self.ro_uri
-        return True
-
-class TFS(object):
-    def __init__(self, nodeurl, root_uri):
-        self.nodeurl = nodeurl
-        self.root_uri = root_uri
-        self.dirs = {}
-
-        self.cache = FileCache(nodeurl, os.path.expanduser('~/.tahoe/_cache'))
-        ro_uri = NewDirectoryURI.init_from_string(self.root_uri).get_readonly()
-        self.root = Directory(ro_uri, self.root_uri)
-        self.load_dir('<root>', self.root)
-
-    def log(self, msg):
-        log("<TFS> %s" % (msg, ))
-
-    def pprint(self):
-        return self.root.pprint()
-
-    def get_parent_name_and_child(self, path):
-        dirname, name = os.path.split(path)
-        parent = self.get_path(dirname)
-        try:
-            child = parent.get_child(name)
-            return parent, name, child
-        except KeyError:
-            return parent, name, None
-        
-    def get_path(self, path):
-        comps = path.strip('/').split('/')
-        if comps == ['']:
-            comps = []
-        cursor = self.root
-        for comp in comps:
-            if not isinstance(cursor, Directory):
-                self.log('path "%s" is not a dir' % (path,))
-                return None
-            try:
-                cursor = cursor.children[comp]
-            except KeyError:
-                self.log('path "%s" not found' % (path,))
-                return None
-        return cursor
-
-    def load_dir(self, name, dirobj):
-        print 'loading', name, dirobj
-        url = self.nodeurl + "uri/%s?t=json" % urllib.quote(dirobj.get_uri())
-        data = urllib.urlopen(url).read()
-        parsed = simplejson.loads(data)
-        nodetype, d = parsed
-        assert nodetype == 'dirnode'
-        for name,details in d['children'].items():
-            name = str(name)
-            ctype, cattrs = details
-            if ctype == 'dirnode':
-                cobj = self.dir_for(name, cattrs.get('ro_uri'), cattrs.get('rw_uri'))
-            else:
-                assert ctype == "filenode"
-                cobj = File(cattrs.get('size'), cattrs.get('ro_uri'), cattrs.get('metadata'))
-            dirobj.children[name] = cobj
-
-    def dir_for(self, name, ro_uri, rw_uri):
-        if ro_uri:
-            ro_uri = str(ro_uri)
-        if rw_uri:
-            rw_uri = str(rw_uri)
-        uri = rw_uri or ro_uri
-        assert uri
-        dirobj = self.dirs.get(uri)
-        if not dirobj:
-            dirobj = Directory(ro_uri, rw_uri)
-            self.dirs[uri] = dirobj
-            self.load_dir(name, dirobj)
-        return dirobj
-
-    def upload(self, fname):
-        self.log('upload(%r)' % (fname,))
-        fh = file(fname, 'rb')
-        url = self.nodeurl + "uri"
-        file_cap = do_http('PUT', url, fh)
-        self.log('uploaded to: %r' % (file_cap,))
-        return file_cap
-
-    def add_child(self, parent_dir_uri, child_name, child_uri):
-        self.log('add_child(%r, %r, %r)' % (parent_dir_uri, child_name, child_uri,))
-        url = self.nodeurl + "uri/%s/%s?t=uri" % (urllib.quote(parent_dir_uri), urllib.quote(child_name), )
-        child_cap = do_http('PUT', url, child_uri)
-        assert child_cap == child_uri
-        self.log('added child %r with %r to %r' % (child_name, child_uri, parent_dir_uri))
-        return child_uri
-
-    def remove_child(self, parent_uri, child_name):
-        self.log('remove_child(%r, %r)' % (parent_uri, child_name, ))
-        url = self.nodeurl + "uri/%s/%s" % (urllib.quote(parent_uri), urllib.quote(child_name))
-        resp = do_http('DELETE', url)
-        self.log('child removal yielded %r' % (resp,))
-
-    def mkdir(self, path):
-        self.log('mkdir(%r)' % (path,))
-        url = self.nodeurl + "uri?t=mkdir"
-        new_dir_cap = do_http('PUT', url)
-        parent_path, name = os.path.split(path)
-        self.log('parent_path, name = %s, %s' % (parent_path, name,))
-        parent = self.get_path(parent_path)
-        self.log('parent = %s' % (parent, ))
-        self.log('new_dir_cap = %s' % (new_dir_cap, ))
-        child_uri = self.add_child(parent.get_uri(), name, new_dir_cap)
-        ro_uri = NewDirectoryURI.init_from_string(child_uri).get_readonly()
-        child = Directory(ro_uri, child_uri)
-        parent.add_child(name, child)
-
-    def rename(self, path, path1):
-        self.log('rename(%s, %s)' % (path, path1))
-        parent, name, child = self.get_parent_name_and_child(path)
-        child_uri = child.get_uri()
-        new_parent_path, new_child_name = os.path.split(path1)
-        new_parent = self.get_path(new_parent_path)
-        self.add_child(new_parent.get_uri(), new_child_name, child_uri)
-        self.remove_child(parent.get_uri(), name)
-        parent.remove_child(name)
-        new_parent.add_child(new_child_name, child)
-
-class FileCache(object):
-    def __init__(self, nodeurl, cachedir):
-        self.nodeurl = nodeurl
-        self.cachedir = cachedir
-        if not os.path.exists(self.cachedir):
-            os.makedirs(self.cachedir)
-        self.tmpdir = os.path.join(self.cachedir, 'tmp')
-        if not os.path.exists(self.tmpdir):
-            os.makedirs(self.tmpdir)
-
-    def log(self, msg):
-        log("<FC> %s" % (msg, ))
-
-    def get_file(self, uri):
-        self.log('get_file(%s)' % (uri,))
-        if uri.startswith("URI:LIT"):
-            return self.get_literal(uri)
-        else:
-            return self.get_chk(uri)
-
-    def get_literal(self, uri):
-        h = sha.new(uri).digest()
-        u = LiteralFileURI.init_from_string(uri)
-        fname = os.path.join(self.cachedir, '__'+base64.b32encode(h).lower())
-        size = len(u.data)
-        self.log('writing literal file %s (%s)' % (fname, size, ))
-        fh = open(fname, 'wb')
-        fh.write(u.data)
-        fh.close()
-        return fname
-
-    def get_chk(self, uri):
-        u = CHKFileURI.init_from_string(str(uri))
-        storage_index = u.storage_index
-        size = u.size
-        fname = os.path.join(self.cachedir, base64.b32encode(storage_index).lower())
-        if os.path.exists(fname):
-            fsize = os.path.getsize(fname)
-            if fsize == size:
-                return fname
-            else:
-                self.log('warning file "%s" is too short %s < %s' % (fname, fsize, size))
-        self.log('downloading file %s (%s)' % (fname, size, ))
-        fh = open(fname, 'wb')
-        url = "%suri/%s" % (self.nodeurl, uri)
-        download = urllib.urlopen(''.join([ self.nodeurl, "uri/", uri ]))
-        while True:
-            chunk = download.read(4096)
-            if not chunk:
-                break
-            fh.write(chunk)
-        fh.close()
-        return fname
-
-    def tmp_file(self, id):
-        fname = os.path.join(self.tmpdir, base64.b32encode(id).lower())
-        return fname
-
-def print_tree():
-    log('tree:\n' + _tfs.pprint())
-
-def main(argv):
-    log("\n\nmain(%s)" % (argv,))
-
-    if not argv:
-        argv = ['--help']
-    if len(argv) == 1 and argv[0] in ['-h', '--help', '--version']:
-        #print USAGE
-        launch_tahoe_fuse(None, argv)
-        return -2
-
-    if not argv[0].startswith('-'):
-        cap_name = argv[0]
-        basedir = getbasedir(cap_name)
-        if basedir is None:
-            print 'root dir named "%s" not found.' % (cap_name,)
-            return -2
-        argv = argv[1:]
-    else:
-        basedir = getbasedir() # default 'root_dir'
-
-    if argv[-1].startswith('-'):
-        print 'mountpoint not given'
-        return -2
-    mountpoint = os.path.abspath(argv[-1])
-    if not os.path.exists(mountpoint):
-        #raise OSError(2, 'No such file or directory: "%s"' % (mountpoint,))
-        print 'No such file or directory: "%s"' % (mountpoint,)
-        return -2
-
-    nodeurl = getnodeurl()
-
-    tfs = TFS(nodeurl, basedir)
-    print tfs.pprint()
-
-    # make tfs instance accesible to print_tree() for dbg
-    global _tfs
-    _tfs = tfs
-
-    launch_tahoe_fuse(tfs, argv)
-
-if __name__ == '__main__':
-    main(sys.argv)
diff --git a/mac/tahoefuse.py b/mac/tahoefuse.py
new file mode 100644 (file)
index 0000000..470898d
--- /dev/null
@@ -0,0 +1,713 @@
+#!/usr/bin/env python
+
+#-----------------------------------------------------------------------------------------------
+from allmydata.uri import CHKFileURI, NewDirectoryURI, LiteralFileURI
+from allmydata.scripts.common_http import do_http as do_http_req
+
+import base64
+import sha
+import sys
+import os
+import errno
+import stat
+# pull in some spaghetti to make this stuff work without fuse-py being installed
+try:
+    import _find_fuse_parts
+    junk = _find_fuse_parts
+    del junk
+except ImportError:
+    pass
+import fuse
+
+import time
+import traceback
+import simplejson
+import urllib
+
+USAGE = 'usage: tahoe fuse [dir_cap_name] [fuse_options] mountpoint'
+
+if not hasattr(fuse, '__version__'):
+    raise RuntimeError, \
+        "your fuse-py doesn't know of fuse.__version__, probably it's too old."
+
+fuse.fuse_python_api = (0, 2)
+fuse.feature_assert('stateful_files', 'has_init')
+
+
+logfile = file('tfuse.log', 'wb')
+
+def log(msg):
+    logfile.write("%s: %s\n" % (time.asctime(), msg))
+    #time.sleep(0.1)
+    logfile.flush()
+
+fuse.flog = log
+
+def do_http(method, url, body=''):
+    resp = do_http_req(method, url, body)
+    log('do_http(%s, %s) -> %s, %s' % (method, url, resp.status, resp.reason))
+    if resp.status not in (200, 201):
+        raise RuntimeError('http response (%s, %s)' % (resp.status, resp.reason))
+    else:
+        return resp.read()
+
+def flag2mode(flags):
+    log('flag2mode(%r)' % (flags,))
+    #md = {os.O_RDONLY: 'r', os.O_WRONLY: 'w', os.O_RDWR: 'w+'}
+    md = {os.O_RDONLY: 'rb', os.O_WRONLY: 'wb', os.O_RDWR: 'w+b'}
+    m = md[flags & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR)]
+
+    if flags | os.O_APPEND:
+        m = m.replace('w', 'a', 1)
+
+    return m
+
+def logargsretexc(meth):
+    def inner(self, *args, **kwargs):
+        log("%s(%r, %r)" % (meth, args, kwargs))
+        try:
+            ret = meth(self, *args, **kwargs)
+        except:
+            log('exception:\n%s' % (traceback.format_exc(),))
+            raise
+        log("ret: %r" % (ret, ))
+        return ret
+    inner.__name__ = '<logwrap(%s)>' % (meth,)
+    return inner
+
+def logexc(meth):
+    def inner(self, *args, **kwargs):
+        try:
+            ret = meth(self, *args, **kwargs)
+        except:
+            log('exception:\n%s' % (traceback.format_exc(),))
+            raise
+        return ret
+    inner.__name__ = '<logwrap(%s)>' % (meth,)
+    return inner
+
+def log_exc():
+    log('exception:\n%s' % (traceback.format_exc(),))
+
+class TahoeFuseFile(object):
+
+    def __init__(self, path, flags, *mode):
+        log("TFF: __init__(%r, %r, %r)" % (path, flags, mode))
+
+        self._path = path # for tahoe put
+        try:
+            self.parent, self.name, self.fnode = self.tfs.get_parent_name_and_child(path)
+            m = flag2mode(flags)
+            log('TFF: flags2(mode) -> %s' % (m,))
+            if m[0] in 'wa':
+                # write
+                self.fname = self.tfs.cache.tmp_file(os.urandom(20))
+                if self.fnode is None:
+                    log('TFF: [%s] open(%s) for write: no such file, creating new File' % (self.name, self.fname, ))
+                    self.fnode = File(0, None, None)
+                    self.fnode.tmp_fname = self.fname # XXX kill this
+                    self.parent.add_child(self.name, self.fnode)
+                elif hasattr(self.fnode, 'tmp_fname'):
+                    self.fname = self.fnode.tmp_fname
+                self.file = os.fdopen(os.open(self.fname, flags, *mode), m)
+                self.fd = self.file.fileno()
+                log('TFF: opened(%s) for write' % self.fname)
+                self.open_for_write = True
+            else:
+                # read
+                assert self.fnode is not None
+                uri = self.fnode.get_uri()
+
+                # XXX make this go away
+                if hasattr(self.fnode, 'tmp_fname'):
+                    self.fname = self.fnode.tmp_fname
+                    log('TFF: reopening(%s) for reading' % self.fname)
+                else:
+                    log('TFF: fetching file from cache for reading')
+                    self.fname = self.tfs.cache.get_file(uri)
+
+                self.file = os.fdopen(os.open(self.fname, flags, *mode), m)
+                self.fd = self.file.fileno()
+                self.open_for_write = False
+                log('TFF: opened(%s) for read' % self.fname)
+        except:
+            log_exc()
+            raise
+
+    def log(self, msg):
+        log("<TFF(%s:%s)> %s" % (os.path.basename(self.fname), self.name, msg))
+
+    @logexc
+    def read(self, size, offset):
+        self.log('read(%r, %r)' % (size, offset, ))
+        self.file.seek(offset)
+        return self.file.read(size)
+
+    @logexc
+    def write(self, buf, offset):
+        self.log("write(-%s-, %r)" % (len(buf), offset))
+        if not self.open_for_write:
+            return -errno.EACCES
+        self.file.seek(offset)
+        self.file.write(buf)
+        return len(buf)
+
+    @logexc
+    def release(self, flags):
+        self.log("release(%r)" % (flags,))
+        self.file.close()
+        if self.open_for_write:
+            size = os.path.getsize(self.fname)
+            self.fnode.size = size
+            file_cap = self.tfs.upload(self.fname)
+            self.fnode.ro_uri = file_cap
+            self.tfs.add_child(self.parent.get_uri(), self.name, file_cap)
+            self.log("uploaded: %s" % (file_cap,))
+
+        # dbg
+        print_tree()
+
+    def _fflush(self):
+        if 'w' in self.file.mode or 'a' in self.file.mode:
+            self.file.flush()
+
+    @logexc
+    def fsync(self, isfsyncfile):
+        self.log("fsync(%r)" % (isfsyncfile,))
+        self._fflush()
+        if isfsyncfile and hasattr(os, 'fdatasync'):
+            os.fdatasync(self.fd)
+        else:
+            os.fsync(self.fd)
+
+    @logexc
+    def flush(self):
+        self.log("flush()")
+        self._fflush()
+        # cf. xmp_flush() in fusexmp_fh.c
+        os.close(os.dup(self.fd))
+
+    @logexc
+    def fgetattr(self):
+        self.log("fgetattr()")
+        s = os.fstat(self.fd)
+        self.log("fgetattr() -> %r" % (s,))
+        return s
+
+    @logexc
+    def ftruncate(self, len):
+        self.log("ftruncate(%r)" % (len,))
+        self.file.truncate(len)
+
+class ObjFetcher(object):
+    def get_tahoe_file(self, path, flags, *mode):
+        log('objfetcher.get_tahoe_file(%r, %r, %r, %r)' % (self, path, flags, mode))
+        return TahoeFuseFile(path, flags, *mode)
+fetcher = ObjFetcher()
+
+class TahoeFuse(fuse.Fuse):
+
+    def __init__(self, tfs, *args, **kw):
+        log("TF: __init__(%r, %r)" % (args, kw))
+
+        self.tfs = tfs
+        _tfs_ = tfs
+        class MyFuseFile(TahoeFuseFile):
+            tfs = _tfs_
+        self.file_class = MyFuseFile
+        log("TF: file_class: %r" % (self.file_class,))
+
+        fuse.Fuse.__init__(self, *args, **kw)
+
+        #import thread
+        #thread.start_new_thread(self.launch_reactor, ())
+
+    def log(self, msg):
+        log("<TF> %s" % (msg, ))
+
+    @logexc
+    def readlink(self, path):
+        self.log("readlink(%r)" % (path,))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def unlink(self, path):
+        self.log("unlink(%r)" % (path,))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def rmdir(self, path):
+        self.log("rmdir(%r)" % (path,))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def symlink(self, path, path1):
+        self.log("symlink(%r, %r)" % (path, path1))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def rename(self, path, path1):
+        self.log("rename(%r, %r)" % (path, path1))
+        self.tfs.rename(path, path1)
+
+    @logexc
+    def link(self, path, path1):
+        self.log("link(%r, %r)" % (path, path1))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def chmod(self, path, mode):
+        self.log("chmod(%r, %r)" % (path, mode))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def chown(self, path, user, group):
+        self.log("chown(%r, %r, %r)" % (path, user, group))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def truncate(self, path, len):
+        self.log("truncate(%r, %r)" % (path, len))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def utime(self, path, times):
+        self.log("utime(%r, %r)" % (path, times))
+        return -errno.EOPNOTSUPP
+
+    @logexc
+    def statfs(self):
+        self.log("statfs()")
+        """
+        Should return an object with statvfs attributes (f_bsize, f_frsize...).
+        Eg., the return value of os.statvfs() is such a thing (since py 2.2).
+        If you are not reusing an existing statvfs object, start with
+        fuse.StatVFS(), and define the attributes.
+
+        To provide usable information (ie., you want sensible df(1)
+        output, you are suggested to specify the following attributes:
+
+            - f_bsize - preferred size of file blocks, in bytes
+            - f_frsize - fundamental size of file blcoks, in bytes
+                [if you have no idea, use the same as blocksize]
+            - f_blocks - total number of blocks in the filesystem
+            - f_bfree - number of free blocks
+            - f_files - total number of file inodes
+            - f_ffree - nunber of free file inodes
+        """
+
+        return os.statvfs(".")
+
+    def fsinit(self):
+        self.log("fsinit()")
+
+    def main(self, *a, **kw):
+        self.log("main(%r, %r)" % (a, kw))
+
+        return fuse.Fuse.main(self, *a, **kw)
+
+    ##################################################################
+
+    @logexc
+    def readdir(self, path, offset):
+        log('readdir(%r, %r)' % (path, offset))
+        node = self.tfs.get_path(path)
+        if node is None:
+            return -errno.ENOENT
+        dirlist = ['.', '..'] + node.children.keys()
+        log('dirlist = %r' % (dirlist,))
+        return [fuse.Direntry(d) for d in dirlist]
+
+    @logexc
+    def getattr(self, path):
+        log('getattr(%r)' % (path,))
+        node = self.tfs.get_path(path)
+        if node is None:
+            return -errno.ENOENT
+        return node.get_stat()
+
+    @logexc
+    def access(self, path, mode):
+        self.log("access(%r, %r)" % (path, mode))
+        node = self.tfs.get_path(path)
+        if not node:
+            return -errno.ENOENT
+        accmode = os.O_RDONLY | os.O_WRONLY | os.O_RDWR
+        if (mode & 0222):
+            if not node.writable():
+                log('write access denied for %s (req:%o)' % (path, mode, ))
+                return -errno.EACCES
+        #else:
+            #log('access granted for %s' % (path, ))
+
+    @logexc
+    def mkdir(self, path, mode):
+        self.log("mkdir(%r, %r)" % (path, mode))
+        self.tfs.mkdir(path)
+
+def launch_tahoe_fuse(tfs, argv):
+    sys.argv = ['tahoe fuse'] + list(argv)
+    server = TahoeFuse(tfs, version="%prog " + fuse.__version__,
+                       usage=USAGE,
+                       dash_s_do='setsingle')
+    server.parse(errex=1)
+    server.main()
+
+
+def getbasedir(cap_name='root_dir'):
+    fname = os.path.expanduser("~/.tahoe/private/%s.cap" % (cap_name,))
+    if os.path.exists(fname):
+        f = file(fname, 'rb')
+        bd = f.read().strip()
+        f.close()
+        return bd
+    else:
+        return None
+
+def getnodeurl():
+    f = file(os.path.expanduser("~/.tahoe/node.url"), 'rb')
+    nu = f.read().strip()
+    f.close()
+    if nu[-1] != "/":
+        nu += "/"
+    return nu
+
+def fingerprint(uri):
+    if uri is None:
+        return None
+    return base64.b32encode(sha.new(uri).digest()).lower()[:6]
+
+class TStat(fuse.Stat):
+    def __init__(self, **kwargs):
+        fuse.Stat.__init__(self, **kwargs)
+
+    def __repr__(self):
+        return "<Stat%r" % {
+            'st_mode': self.st_mode,
+            'st_ino': self.st_ino,
+            'st_dev': self.st_dev,
+            'st_nlink': self.st_nlink,
+            'st_uid': self.st_uid,
+            'st_gid': self.st_gid,
+            'st_size': self.st_size,
+            'st_atime': self.st_atime,
+            'st_mtime': self.st_mtime,
+            'st_ctime': self.st_ctime,
+            }
+
+class Directory(object):
+    def __init__(self, ro_uri, rw_uri):
+        self.ro_uri = ro_uri
+        self.rw_uri = rw_uri
+        assert (rw_uri or ro_uri)
+        self.children = {}
+
+    def __repr__(self):
+        return "<Directory %s>" % (fingerprint(self.get_uri()),)
+
+    def get_children(self):
+        return self.children.keys()
+
+    def get_child(self, name):
+        return self.children[name]
+
+    def add_child(self, name, file_node):
+        self.children[name] = file_node
+
+    def remove_child(self, name):
+        del self.children[name]
+
+    def get_uri(self):
+        return self.rw_uri or self.ro_uri
+
+    def writable(self):
+        return self.rw_uri and self.rw_uri != self.ro_uri
+
+    def pprint(self, prefix='', printed=None):
+        ret = []
+        if printed is None:
+            printed = set()
+        writable = self.writable() and '+' or ' '
+        if self in printed:
+            ret.append("         %s/%s ... <%s>" % (prefix, writable, fingerprint(self.get_uri()), ))
+        else:
+            ret.append("[%s] %s/%s" % (fingerprint(self.get_uri()), prefix, writable, ))
+            printed.add(self)
+            for name,f in sorted(self.children.items()):
+                ret.append(f.pprint(' ' * (len(prefix)+1)+name, printed))
+        return '\n'.join(ret)
+
+    def get_stat(self):
+        s = TStat(st_mode = stat.S_IFDIR | 0755, st_nlink = 2)
+        log("%s.get_stat()->%s" % (self, s))
+        return s
+
+class File(object):
+    def __init__(self, size, ro_uri, metadata):
+        self.size = size
+        if ro_uri:
+            ro_uri = str(ro_uri)
+        self.ro_uri = ro_uri
+        self.metadata = metadata or {}
+
+    def __repr__(self):
+        return "<File %s>" % (fingerprint(self.ro_uri) or [self.tmp_fname],)
+
+    def pprint(self, prefix='', printed=None):
+        times, remainder = self.get_times()
+        return "         %s (%s) %s %s" % (prefix, self.size, times, remainder)
+
+    def get_times(self):
+        rem = self.metadata.copy()
+        now = time.time()
+        times = {}
+        for T in ['c', 'm']:
+            t = rem.pop('%stime'%T, None)
+            if not t:
+                t = 'none'
+            elif (now-t) < 86400:
+                t = time.strftime('%a:%H:%M:%S', time.localtime(t))
+            else:
+                t = time.strftime('%Y:%b:%d:%H:%M', time.localtime(t))
+            times[T] = t
+        return times, rem
+
+    def get_stat(self):
+        if hasattr(self, 'tmp_fname'):
+            s = os.stat(self.tmp_fname)
+            log("%s.get_stat()->%s" % (self, s))
+        else:
+            s = TStat(st_size=self.size, st_mode = stat.S_IFREG | 0444, st_nlink = 1)
+            log("%s.get_stat()->%s" % (self, s))
+        return s
+
+    def get_uri(self):
+        return self.ro_uri
+
+    def writable(self):
+        #return not self.ro_uri
+        return True
+
+class TFS(object):
+    def __init__(self, nodeurl, root_uri):
+        self.nodeurl = nodeurl
+        self.root_uri = root_uri
+        self.dirs = {}
+
+        self.cache = FileCache(nodeurl, os.path.expanduser('~/.tahoe/_cache'))
+        ro_uri = NewDirectoryURI.init_from_string(self.root_uri).get_readonly()
+        self.root = Directory(ro_uri, self.root_uri)
+        self.load_dir('<root>', self.root)
+
+    def log(self, msg):
+        log("<TFS> %s" % (msg, ))
+
+    def pprint(self):
+        return self.root.pprint()
+
+    def get_parent_name_and_child(self, path):
+        dirname, name = os.path.split(path)
+        parent = self.get_path(dirname)
+        try:
+            child = parent.get_child(name)
+            return parent, name, child
+        except KeyError:
+            return parent, name, None
+        
+    def get_path(self, path):
+        comps = path.strip('/').split('/')
+        if comps == ['']:
+            comps = []
+        cursor = self.root
+        for comp in comps:
+            if not isinstance(cursor, Directory):
+                self.log('path "%s" is not a dir' % (path,))
+                return None
+            try:
+                cursor = cursor.children[comp]
+            except KeyError:
+                self.log('path "%s" not found' % (path,))
+                return None
+        return cursor
+
+    def load_dir(self, name, dirobj):
+        print 'loading', name, dirobj
+        url = self.nodeurl + "uri/%s?t=json" % urllib.quote(dirobj.get_uri())
+        data = urllib.urlopen(url).read()
+        parsed = simplejson.loads(data)
+        nodetype, d = parsed
+        assert nodetype == 'dirnode'
+        for name,details in d['children'].items():
+            name = str(name)
+            ctype, cattrs = details
+            if ctype == 'dirnode':
+                cobj = self.dir_for(name, cattrs.get('ro_uri'), cattrs.get('rw_uri'))
+            else:
+                assert ctype == "filenode"
+                cobj = File(cattrs.get('size'), cattrs.get('ro_uri'), cattrs.get('metadata'))
+            dirobj.children[name] = cobj
+
+    def dir_for(self, name, ro_uri, rw_uri):
+        if ro_uri:
+            ro_uri = str(ro_uri)
+        if rw_uri:
+            rw_uri = str(rw_uri)
+        uri = rw_uri or ro_uri
+        assert uri
+        dirobj = self.dirs.get(uri)
+        if not dirobj:
+            dirobj = Directory(ro_uri, rw_uri)
+            self.dirs[uri] = dirobj
+            self.load_dir(name, dirobj)
+        return dirobj
+
+    def upload(self, fname):
+        self.log('upload(%r)' % (fname,))
+        fh = file(fname, 'rb')
+        url = self.nodeurl + "uri"
+        file_cap = do_http('PUT', url, fh)
+        self.log('uploaded to: %r' % (file_cap,))
+        return file_cap
+
+    def add_child(self, parent_dir_uri, child_name, child_uri):
+        self.log('add_child(%r, %r, %r)' % (parent_dir_uri, child_name, child_uri,))
+        url = self.nodeurl + "uri/%s/%s?t=uri" % (urllib.quote(parent_dir_uri), urllib.quote(child_name), )
+        child_cap = do_http('PUT', url, child_uri)
+        assert child_cap == child_uri
+        self.log('added child %r with %r to %r' % (child_name, child_uri, parent_dir_uri))
+        return child_uri
+
+    def remove_child(self, parent_uri, child_name):
+        self.log('remove_child(%r, %r)' % (parent_uri, child_name, ))
+        url = self.nodeurl + "uri/%s/%s" % (urllib.quote(parent_uri), urllib.quote(child_name))
+        resp = do_http('DELETE', url)
+        self.log('child removal yielded %r' % (resp,))
+
+    def mkdir(self, path):
+        self.log('mkdir(%r)' % (path,))
+        url = self.nodeurl + "uri?t=mkdir"
+        new_dir_cap = do_http('PUT', url)
+        parent_path, name = os.path.split(path)
+        self.log('parent_path, name = %s, %s' % (parent_path, name,))
+        parent = self.get_path(parent_path)
+        self.log('parent = %s' % (parent, ))
+        self.log('new_dir_cap = %s' % (new_dir_cap, ))
+        child_uri = self.add_child(parent.get_uri(), name, new_dir_cap)
+        ro_uri = NewDirectoryURI.init_from_string(child_uri).get_readonly()
+        child = Directory(ro_uri, child_uri)
+        parent.add_child(name, child)
+
+    def rename(self, path, path1):
+        self.log('rename(%s, %s)' % (path, path1))
+        parent, name, child = self.get_parent_name_and_child(path)
+        child_uri = child.get_uri()
+        new_parent_path, new_child_name = os.path.split(path1)
+        new_parent = self.get_path(new_parent_path)
+        self.add_child(new_parent.get_uri(), new_child_name, child_uri)
+        self.remove_child(parent.get_uri(), name)
+        parent.remove_child(name)
+        new_parent.add_child(new_child_name, child)
+
+class FileCache(object):
+    def __init__(self, nodeurl, cachedir):
+        self.nodeurl = nodeurl
+        self.cachedir = cachedir
+        if not os.path.exists(self.cachedir):
+            os.makedirs(self.cachedir)
+        self.tmpdir = os.path.join(self.cachedir, 'tmp')
+        if not os.path.exists(self.tmpdir):
+            os.makedirs(self.tmpdir)
+
+    def log(self, msg):
+        log("<FC> %s" % (msg, ))
+
+    def get_file(self, uri):
+        self.log('get_file(%s)' % (uri,))
+        if uri.startswith("URI:LIT"):
+            return self.get_literal(uri)
+        else:
+            return self.get_chk(uri)
+
+    def get_literal(self, uri):
+        h = sha.new(uri).digest()
+        u = LiteralFileURI.init_from_string(uri)
+        fname = os.path.join(self.cachedir, '__'+base64.b32encode(h).lower())
+        size = len(u.data)
+        self.log('writing literal file %s (%s)' % (fname, size, ))
+        fh = open(fname, 'wb')
+        fh.write(u.data)
+        fh.close()
+        return fname
+
+    def get_chk(self, uri):
+        u = CHKFileURI.init_from_string(str(uri))
+        storage_index = u.storage_index
+        size = u.size
+        fname = os.path.join(self.cachedir, base64.b32encode(storage_index).lower())
+        if os.path.exists(fname):
+            fsize = os.path.getsize(fname)
+            if fsize == size:
+                return fname
+            else:
+                self.log('warning file "%s" is too short %s < %s' % (fname, fsize, size))
+        self.log('downloading file %s (%s)' % (fname, size, ))
+        fh = open(fname, 'wb')
+        url = "%suri/%s" % (self.nodeurl, uri)
+        download = urllib.urlopen(''.join([ self.nodeurl, "uri/", uri ]))
+        while True:
+            chunk = download.read(4096)
+            if not chunk:
+                break
+            fh.write(chunk)
+        fh.close()
+        return fname
+
+    def tmp_file(self, id):
+        fname = os.path.join(self.tmpdir, base64.b32encode(id).lower())
+        return fname
+
+def print_tree():
+    log('tree:\n' + _tfs.pprint())
+
+def main(argv):
+    log("\n\nmain(%s)" % (argv,))
+
+    if not argv:
+        argv = ['--help']
+    if len(argv) == 1 and argv[0] in ['-h', '--help', '--version']:
+        #print USAGE
+        launch_tahoe_fuse(None, argv)
+        return -2
+
+    if not argv[0].startswith('-'):
+        cap_name = argv[0]
+        basedir = getbasedir(cap_name)
+        if basedir is None:
+            print 'root dir named "%s" not found.' % (cap_name,)
+            return -2
+        argv = argv[1:]
+    else:
+        basedir = getbasedir() # default 'root_dir'
+
+    if argv[-1].startswith('-'):
+        print 'mountpoint not given'
+        return -2
+    mountpoint = os.path.abspath(argv[-1])
+    if not os.path.exists(mountpoint):
+        #raise OSError(2, 'No such file or directory: "%s"' % (mountpoint,))
+        print 'No such file or directory: "%s"' % (mountpoint,)
+        return -2
+
+    nodeurl = getnodeurl()
+
+    tfs = TFS(nodeurl, basedir)
+    print tfs.pprint()
+
+    # make tfs instance accesible to print_tree() for dbg
+    global _tfs
+    _tfs = tfs
+
+    launch_tahoe_fuse(tfs, argv)
+
+if __name__ == '__main__':
+    main(sys.argv)