docs: def compare(localtopdir, jottamountpoint, JFS, followlinks=False, exclude_patterns=None):
      """Make a tree of local files and folders and compare it with what's currently on JottaCloud.
  
      For each folder, yields:
          dirpath, # byte string, full path
          onlylocal, # set(), files that only exist locally, i.e. newly added files that don't exist online,
          onlyremote, # set(), files that only exist in the JottaCloud, i.e. deleted locally
          bothplaces # set(), files that exist both locally and remotely
          onlyremotefolders, # set(), folders that only exist in the JottaCloud, i.e. deleted locally
      """
      def excluded(unicodepath, fname):
          fpath = os.path.join(unicodepath, _decode_filename_to_unicode(fname))
          # skip FIFOs, block devices, character devices and the like, see bug#129
          mode = os.stat(fpath).st_mode
          if not (stat.S_ISREG(mode) or 
                  stat.S_ISLNK(mode) or
                  stat.S_ISDIR(mode)): # we only like regular files, dirs or symlinks
              return True
          if exclude_patterns is None:
              return False
          for p in exclude_patterns:
              if p.search(fpath):
                  log.debug("%r excluded by pattern %r", fpath, p.pattern)
                  return True
          return False
      bytestring_localtopdir = _encode_filename_to_filesystem(localtopdir)
      for dirpath, dirnames, filenames in os.walk(bytestring_localtopdir, followlinks=followlinks):
          # to keep things explicit, and avoid encoding/decoding issues,
          # keep a bytestring AND a unicode variant of dirpath
          dirpath = _encode_filename_to_filesystem(dirpath)
          unicodepath = _decode_filename_to_unicode(dirpath)
          log.debug("compare walk: %r -> %s files ", unicodepath, len(filenames))
  
          # create set()s of local files and folders
          # paths will be unicode strings
          localfiles = set([f for f in filenames if not excluded(unicodepath, f)]) # these are on local disk
          localfolders = set([f for f in dirnames if not excluded(unicodepath, f)]) # these are on local disk
          jottapath = get_jottapath(localtopdir, unicodepath, jottamountpoint) # translate to jottapath
          log.debug("compare jottapath: %r", jottapath)
  
          # create set()s of remote files and folders
          # paths will be unicode strings
          cloudfiles = filelist(jottapath, JFS) # set(). these are on jottacloud
          cloudfolders = folderlist(jottapath, JFS)
  
          log.debug("--cloudfiles: %r", cloudfiles)
          log.debug("--localfiles: %r", localfiles)
          log.debug("--cloudfolders: %r", cloudfolders)
  
          onlylocal = [ sf(f, dirpath, jottapath) for f in localfiles.difference(cloudfiles)]
          onlyremote = [ sf(f, dirpath, jottapath) for f in cloudfiles.difference(localfiles)]
          bothplaces = [ sf(f, dirpath, jottapath) for f in localfiles.intersection(cloudfiles)]
          onlyremotefolders = [ sf(f, dirpath, jottapath) for f in cloudfolders.difference(localfolders)]
          yield dirpath, onlylocal, onlyremote, bothplaces, onlyremotefolders