gio/ docs/reference/gio Merged gio-standalone into glib.
authorAlexander Larsson <alexl@redhat.com>
Mon, 26 Nov 2007 16:13:05 +0000 (16:13 +0000)
committerAlexander Larsson <alexl@src.gnome.org>
Mon, 26 Nov 2007 16:13:05 +0000 (16:13 +0000)
2007-11-26  Alexander Larsson  <alexl@redhat.com>

        * Makefile.am:
        * configure.in:
        * gio-2.0-uninstalled.pc.in:
        * gio-2.0.pc.in:
        * gio-unix-2.0-uninstalled.pc.in:
        * gio-unix-2.0.pc.in:
* gio/
* docs/reference/gio
Merged gio-standalone into glib.

        * glib/glibintl.h:
        * glib/gutils.c:
Export glib_gettext so that gio can use it
Add P_ (using same domain for now)
Add I_ as g_intern_static_string

svn path=/trunk/; revision=5941

183 files changed:
ChangeLog
Makefile.am
configure.in
docs/reference/ChangeLog
docs/reference/Makefile.am
docs/reference/gio/Makefile.am [new file with mode: 0644]
docs/reference/gio/gio-docs.xml [new file with mode: 0644]
docs/reference/gio/gio-sections.txt [new file with mode: 0644]
docs/reference/gio/gio.types [new file with mode: 0644]
docs/reference/gio/version.xml.in [new file with mode: 0644]
gio-2.0-uninstalled.pc.in [new file with mode: 0644]
gio-2.0.pc.in [new file with mode: 0644]
gio-unix-2.0-uninstalled.pc.in [new file with mode: 0644]
gio-unix-2.0.pc.in [new file with mode: 0644]
gio/Makefile.am [new file with mode: 0644]
gio/fam/Makefile.am [new file with mode: 0644]
gio/fam/fam-helper.c [new file with mode: 0644]
gio/fam/fam-helper.h [new file with mode: 0644]
gio/fam/fam-module.c [new file with mode: 0644]
gio/fam/gfamdirectorymonitor.c [new file with mode: 0644]
gio/fam/gfamdirectorymonitor.h [new file with mode: 0644]
gio/fam/gfamfilemonitor.c [new file with mode: 0644]
gio/fam/gfamfilemonitor.h [new file with mode: 0644]
gio/gappinfo.c [new file with mode: 0644]
gio/gappinfo.h [new file with mode: 0644]
gio/gasynchelper.c [new file with mode: 0644]
gio/gasynchelper.h [new file with mode: 0644]
gio/gasyncresult.c [new file with mode: 0644]
gio/gasyncresult.h [new file with mode: 0644]
gio/gbufferedinputstream.c [new file with mode: 0644]
gio/gbufferedinputstream.h [new file with mode: 0644]
gio/gbufferedoutputstream.c [new file with mode: 0644]
gio/gbufferedoutputstream.h [new file with mode: 0644]
gio/gcancellable.c [new file with mode: 0644]
gio/gcancellable.h [new file with mode: 0644]
gio/gcontenttype.c [new file with mode: 0644]
gio/gcontenttype.h [new file with mode: 0644]
gio/gcontenttypeprivate.h [new file with mode: 0644]
gio/gdatainputstream.c [new file with mode: 0644]
gio/gdatainputstream.h [new file with mode: 0644]
gio/gdataoutputstream.c [new file with mode: 0644]
gio/gdataoutputstream.h [new file with mode: 0644]
gio/gdesktopappinfo.c [new file with mode: 0644]
gio/gdesktopappinfo.h [new file with mode: 0644]
gio/gdirectorymonitor.c [new file with mode: 0644]
gio/gdirectorymonitor.h [new file with mode: 0644]
gio/gdrive.c [new file with mode: 0644]
gio/gdrive.h [new file with mode: 0644]
gio/gdriveprivate.h [new file with mode: 0644]
gio/gdummyfile.c [new file with mode: 0644]
gio/gdummyfile.h [new file with mode: 0644]
gio/gfile.c [new file with mode: 0644]
gio/gfile.h [new file with mode: 0644]
gio/gfileattribute.c [new file with mode: 0644]
gio/gfileattribute.h [new file with mode: 0644]
gio/gfileenumerator.c [new file with mode: 0644]
gio/gfileenumerator.h [new file with mode: 0644]
gio/gfileicon.c [new file with mode: 0644]
gio/gfileicon.h [new file with mode: 0644]
gio/gfileinfo.c [new file with mode: 0644]
gio/gfileinfo.h [new file with mode: 0644]
gio/gfileinputstream.c [new file with mode: 0644]
gio/gfileinputstream.h [new file with mode: 0644]
gio/gfilemonitor.c [new file with mode: 0644]
gio/gfilemonitor.h [new file with mode: 0644]
gio/gfilenamecompleter.c [new file with mode: 0644]
gio/gfilenamecompleter.h [new file with mode: 0644]
gio/gfileoutputstream.c [new file with mode: 0644]
gio/gfileoutputstream.h [new file with mode: 0644]
gio/gfilterinputstream.c [new file with mode: 0644]
gio/gfilterinputstream.h [new file with mode: 0644]
gio/gfilteroutputstream.c [new file with mode: 0644]
gio/gfilteroutputstream.h [new file with mode: 0644]
gio/gicon.c [new file with mode: 0644]
gio/gicon.h [new file with mode: 0644]
gio/ginputstream.c [new file with mode: 0644]
gio/ginputstream.h [new file with mode: 0644]
gio/gio-marshal.list [new file with mode: 0644]
gio/gioerror.c [new file with mode: 0644]
gio/gioerror.h [new file with mode: 0644]
gio/giomodule.c [new file with mode: 0644]
gio/giomodule.h [new file with mode: 0644]
gio/gioscheduler.c [new file with mode: 0644]
gio/gioscheduler.h [new file with mode: 0644]
gio/gloadableicon.c [new file with mode: 0644]
gio/gloadableicon.h [new file with mode: 0644]
gio/glocaldirectorymonitor.c [new file with mode: 0644]
gio/glocaldirectorymonitor.h [new file with mode: 0644]
gio/glocalfile.c [new file with mode: 0644]
gio/glocalfile.h [new file with mode: 0644]
gio/glocalfileenumerator.c [new file with mode: 0644]
gio/glocalfileenumerator.h [new file with mode: 0644]
gio/glocalfileinfo.c [new file with mode: 0644]
gio/glocalfileinfo.h [new file with mode: 0644]
gio/glocalfileinputstream.c [new file with mode: 0644]
gio/glocalfileinputstream.h [new file with mode: 0644]
gio/glocalfilemonitor.c [new file with mode: 0644]
gio/glocalfilemonitor.h [new file with mode: 0644]
gio/glocalfileoutputstream.c [new file with mode: 0644]
gio/glocalfileoutputstream.h [new file with mode: 0644]
gio/glocalvfs.c [new file with mode: 0644]
gio/glocalvfs.h [new file with mode: 0644]
gio/gmemoryinputstream.c [new file with mode: 0644]
gio/gmemoryinputstream.h [new file with mode: 0644]
gio/gmemoryoutputstream.c [new file with mode: 0644]
gio/gmemoryoutputstream.h [new file with mode: 0644]
gio/gmountoperation.c [new file with mode: 0644]
gio/gmountoperation.h [new file with mode: 0644]
gio/gnativevolumemonitor.c [new file with mode: 0644]
gio/gnativevolumemonitor.h [new file with mode: 0644]
gio/goutputstream.c [new file with mode: 0644]
gio/goutputstream.h [new file with mode: 0644]
gio/gpollfilemonitor.c [new file with mode: 0644]
gio/gpollfilemonitor.h [new file with mode: 0644]
gio/gseekable.c [new file with mode: 0644]
gio/gseekable.h [new file with mode: 0644]
gio/gsimpleasyncresult.c [new file with mode: 0644]
gio/gsimpleasyncresult.h [new file with mode: 0644]
gio/gsocketinputstream.c [new file with mode: 0644]
gio/gsocketinputstream.h [new file with mode: 0644]
gio/gsocketoutputstream.c [new file with mode: 0644]
gio/gsocketoutputstream.h [new file with mode: 0644]
gio/gthemedicon.c [new file with mode: 0644]
gio/gthemedicon.h [new file with mode: 0644]
gio/gunionvolumemonitor.c [new file with mode: 0644]
gio/gunionvolumemonitor.h [new file with mode: 0644]
gio/gunixdrive.c [new file with mode: 0644]
gio/gunixdrive.h [new file with mode: 0644]
gio/gunixmounts.c [new file with mode: 0644]
gio/gunixmounts.h [new file with mode: 0644]
gio/gunixvolume.c [new file with mode: 0644]
gio/gunixvolume.h [new file with mode: 0644]
gio/gunixvolumemonitor.c [new file with mode: 0644]
gio/gunixvolumemonitor.h [new file with mode: 0644]
gio/gurifuncs.c [new file with mode: 0644]
gio/gurifuncs.h [new file with mode: 0644]
gio/gvfs.c [new file with mode: 0644]
gio/gvfs.h [new file with mode: 0644]
gio/gvolume.c [new file with mode: 0644]
gio/gvolume.h [new file with mode: 0644]
gio/gvolumemonitor.c [new file with mode: 0644]
gio/gvolumemonitor.h [new file with mode: 0644]
gio/gvolumeprivate.h [new file with mode: 0644]
gio/gwin32appinfo.c [new file with mode: 0644]
gio/gwin32appinfo.h [new file with mode: 0644]
gio/inotify/Makefile.am [new file with mode: 0644]
gio/inotify/ginotifydirectorymonitor.c [new file with mode: 0644]
gio/inotify/ginotifydirectorymonitor.h [new file with mode: 0644]
gio/inotify/ginotifyfilemonitor.c [new file with mode: 0644]
gio/inotify/ginotifyfilemonitor.h [new file with mode: 0644]
gio/inotify/inotify-diag.c [new file with mode: 0644]
gio/inotify/inotify-diag.h [new file with mode: 0644]
gio/inotify/inotify-helper.c [new file with mode: 0644]
gio/inotify/inotify-helper.h [new file with mode: 0644]
gio/inotify/inotify-kernel.c [new file with mode: 0644]
gio/inotify/inotify-kernel.h [new file with mode: 0644]
gio/inotify/inotify-missing.c [new file with mode: 0644]
gio/inotify/inotify-missing.h [new file with mode: 0644]
gio/inotify/inotify-path.c [new file with mode: 0644]
gio/inotify/inotify-path.h [new file with mode: 0644]
gio/inotify/inotify-sub.c [new file with mode: 0644]
gio/inotify/inotify-sub.h [new file with mode: 0644]
gio/inotify/local_inotify.h [new file with mode: 0644]
gio/inotify/local_inotify_syscalls.h [new file with mode: 0644]
gio/xdgmime/.gitignore [new file with mode: 0644]
gio/xdgmime/Makefile.am [new file with mode: 0644]
gio/xdgmime/test-mime.c [new file with mode: 0644]
gio/xdgmime/xdgmime.c [new file with mode: 0644]
gio/xdgmime/xdgmime.h [new file with mode: 0644]
gio/xdgmime/xdgmimealias.c [new file with mode: 0644]
gio/xdgmime/xdgmimealias.h [new file with mode: 0644]
gio/xdgmime/xdgmimecache.c [new file with mode: 0644]
gio/xdgmime/xdgmimecache.h [new file with mode: 0644]
gio/xdgmime/xdgmimeglob.c [new file with mode: 0644]
gio/xdgmime/xdgmimeglob.h [new file with mode: 0644]
gio/xdgmime/xdgmimeint.c [new file with mode: 0644]
gio/xdgmime/xdgmimeint.h [new file with mode: 0644]
gio/xdgmime/xdgmimemagic.c [new file with mode: 0644]
gio/xdgmime/xdgmimemagic.h [new file with mode: 0644]
gio/xdgmime/xdgmimeparent.c [new file with mode: 0644]
gio/xdgmime/xdgmimeparent.h [new file with mode: 0644]
po/ChangeLog
po/POTFILES.in

index 9e864ca..f355a81 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,21 @@
+2007-11-26  Alexander Larsson  <alexl@redhat.com>
+
+        * Makefile.am:
+        * configure.in:
+        * gio-2.0-uninstalled.pc.in:
+        * gio-2.0.pc.in: 
+        * gio-unix-2.0-uninstalled.pc.in:
+        * gio-unix-2.0.pc.in:
+       * gio/
+       * docs/reference/gio
+       Merged gio-standalone into glib.
+       
+        * glib/glibintl.h:
+        * glib/gutils.c:
+       Export glib_gettext so that gio can use it
+       Add P_ (using same domain for now)
+       Add I_ as g_intern_static_string
+
 2007-11-26  Tor Lillqvist  <tml@novell.com>
 
        * glib/win_iconv.c: ISO8859-1 is CP28591, not CP1252.
index 80a1875..af74ec9 100644 (file)
@@ -3,7 +3,7 @@ include $(top_srcdir)/Makefile.decl
 
 AUTOMAKE_OPTIONS = 1.7
 
-SUBDIRS = . m4macros glib gobject gmodule gthread tests build po docs
+SUBDIRS = . m4macros glib gobject gmodule gthread gio tests build po docs
 
 bin_SCRIPTS = glib-gettextize
 
@@ -45,11 +45,15 @@ EXTRA_DIST +=                       \
        gmodule-export-2.0.pc.in        \
        gmodule-no-export-2.0.pc.in     \
        gthread-2.0.pc.in       \
+       gio-2.0.pc.in           \
+       gio-unix-2.0.pc.in      \
        glib-2.0-uninstalled.pc.in      \
        gobject-2.0-uninstalled.pc.in   \
        gmodule-2.0-uninstalled.pc.in   \
        gthread-2.0-uninstalled.pc.in   \
-       gmodule-no-export-2.0-uninstalled.pc.in
+       gmodule-no-export-2.0-uninstalled.pc.in \
+       gio-2.0-uninstalled.pc.in               \
+       gio-unix-2.0-uninstalled.pc.in  
 
 
 # These may be in the builddir too
@@ -77,7 +81,7 @@ stamp-gc-h: config.status
        echo timestamp > stamp-gc-h
 
 pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = glib-2.0.pc gobject-2.0.pc gmodule-2.0.pc gmodule-export-2.0.pc gmodule-no-export-2.0.pc gthread-2.0.pc
+pkgconfig_DATA = glib-2.0.pc gobject-2.0.pc gmodule-2.0.pc gmodule-export-2.0.pc gmodule-no-export-2.0.pc gthread-2.0.pc gio-2.0.pc gio-unix-2.0.pc
 
 $(pkgconfig_DATA): config.status
 
index 2f5c226..2048a4e 100644 (file)
@@ -822,9 +822,15 @@ AM_CONDITIONAL(HAVE_SUNSTUDIO_VISIBILITY, [test x$g_have_sunstudio_visibility =
 AC_C_BIGENDIAN
 
 # check for header files
-AC_CHECK_HEADERS([dirent.h float.h limits.h pwd.h sys/param.h sys/poll.h sys/resource.h])
+AC_CHECK_HEADERS([dirent.h float.h limits.h pwd.h grp.h sys/param.h sys/poll.h sys/resource.h])
 AC_CHECK_HEADERS([sys/time.h sys/times.h sys/wait.h unistd.h values.h])
 AC_CHECK_HEADERS([sys/select.h sys/types.h stdint.h sched.h malloc.h])
+AC_CHECK_HEADERS([sys/vfs.h sys/mount.h sys/vmount.h sys/statfs.h sys/statvfs.h])
+AC_CHECK_HEADERS([mntent.h sys/mnttab.h sys/vfstab.h sys/mntctl.h sys/sysctl.h fstab.h])
+
+# check for structure fields
+AC_CHECK_MEMBERS([struct stat.st_mtimensec, struct stat.st_mtim.tv_nsec, struct stat.st_atimensec, struct stat.st_atim.tv_nsec, struct stat.st_ctimensec, struct stat.st_ctim.tv_nsec])
+AC_CHECK_MEMBERS([struct stat.st_blksize, struct stat.st_blocks])
 
 # Checks for libcharset
 jm_LANGINFO_CODESET
@@ -888,6 +894,8 @@ AC_MSG_RESULT(unsigned $glib_size_type)
 
 # Check for some functions
 AC_CHECK_FUNCS(lstat strerror strsignal memmove vsnprintf stpcpy strcasecmp strncasecmp poll getcwd vasprintf setenv unsetenv getc_unlocked readlink symlink fdwalk)
+AC_CHECK_FUNCS(chown lchown fchmod fchown link statvfs statfs utimes getgrgid getpwuid)
+AC_CHECK_FUNCS(setmntent endmntent hasmntopt getmntinfo)
 # Check for high-resolution sleep functions
 AC_CHECK_FUNCS(nanosleep nsleep)
 
@@ -897,6 +905,47 @@ AC_CHECK_FUNCS(_NSGetEnviron)
 AC_FUNC_VSNPRINTF_C99
 AC_FUNC_PRINTF_UNIX98
 
+dnl
+dnl if statfs() takes 2 arguments (Posix) or 4 (Solaris)
+dnl
+if test "$ac_cv_func_statfs" = yes ; then
+  AC_MSG_CHECKING([number of arguments to statfs()])
+  AC_TRY_COMPILE([#include <unistd.h>
+  #ifdef HAVE_SYS_PARAM_H
+  #include <sys/param.h>
+  #endif
+  #ifdef HAVE_SYS_VFS_H
+  #include <sys/vfs.h>
+  #endif
+  #ifdef HAVE_SYS_MOUNT_H
+  #include <sys/mount.h>
+  #endif
+  #ifdef HAVE_SYS_STATFS_H
+  #include <sys/statfs.h>
+  #endif], [struct statfs st;
+  statfs(NULL, &st);],[
+    AC_MSG_RESULT([2])
+    AC_DEFINE(STATFS_ARGS, 2, [Number of arguments to statfs()])],[
+    AC_TRY_COMPILE([#include <unistd.h>
+  #ifdef HAVE_SYS_PARAM_H
+  #include <sys/param.h>
+  #endif
+  #ifdef HAVE_SYS_VFS_H
+  #include <sys/vfs.h>
+  #endif
+  #ifdef HAVE_SYS_MOUNT_H
+  #include <sys/mount.h>
+  #endif
+  #ifdef HAVE_SYS_STATFS_H
+  #include <sys/statfs.h>
+  #endif], [struct statfs st;
+  statfs(NULL, &st, sizeof (st), 0);],[
+      AC_MSG_RESULT([4])
+      AC_DEFINE(STATFS_ARGS, 4, [Number of arguments to statfs()])],[
+      AC_MSG_RESULT(unknown)
+      AC_MSG_ERROR([unable to determine number of arguments to statfs()])])])
+fi
+
 #
 # Check whether to use an included printf
 #
@@ -1399,6 +1448,105 @@ esac
 AC_MSG_RESULT($GIO)
 AC_SUBST(GIO)
 
+
+dnl **********************************
+dnl *** Check for libselinux (GIO) ***
+dnl **********************************
+AC_ARG_ENABLE(selinux, [  --disable-selinux       build without selinux support])
+msg_selinux=no
+SELINUX_LIBS=
+if test "x$enable_selinux" != "xno"; then
+
+ AC_CHECK_LIB(selinux, is_selinux_enabled,
+   [AC_CHECK_HEADERS(selinux/selinux.h,
+     [AC_SEARCH_LIBS(lgetfilecon_raw, selinux, 
+       [AC_DEFINE(HAVE_SELINUX, 1, [Define to 1 if libselinux is available])
+        SELINUX_LIBS="-lselinux"
+        msg_selinux=yes])
+     ])
+   ])
+fi
+AC_SUBST(SELINUX_LIBS)
+
+dnl *****************************
+dnl ** Check for inotify (GIO) **
+dnl *****************************
+inotify_support=no
+AC_CHECK_HEADERS([linux/inotify.h],
+[
+       inotify_support=yes
+])
+AC_CHECK_HEADERS([sys/inotify.h],
+[
+       inotify_support=yes
+])
+
+AM_CONDITIONAL(HAVE_INOTIFY, [test "$inotify_support" = "yes"])
+
+dnl ****************************
+dnl *** Checks for FAM (GIO) ***
+dnl ****************************
+
+should_disable_fam=no
+
+AC_ARG_ENABLE(fam, [  --disable-fam          build without enabling fam for file system monitoring],
+                         [
+                                if test "x$enable_fam" = "xno"; then
+                                        should_disable_fam=yes
+                                        echo "Not building FAM support"
+                                fi
+                         ]
+                         )
+fam_support=no
+FAM_LIBS=
+if test "x$should_disable_fam" = "xno"; then
+AC_CHECK_LIB(fam, FAMOpen,
+  [AC_CHECK_HEADERS(fam.h,
+    [AC_DEFINE(HAVE_FAM, [], [Define if we have FAM])
+     AC_CHECK_LIB(fam, FAMNoExists,
+                 AC_DEFINE(HAVE_FAM_NO_EXISTS, [], [Define if we have FAMNoExists in fam]))
+     FAM_LIBS="-lfam"]
+     fam_support=yes,
+    AC_MSG_WARN(*** FAM support will not be built (header files not found) ***))],
+  AC_MSG_WARN(*** FAM support will not be built (FAM library not found) ***))
+AC_SUBST(FAM_LIBS)
+fi
+AM_CONDITIONAL(HAVE_FAM, [test "$fam_support" = "yes"])
+
+
+dnl *****************************
+dnl *** Check for xattr (GIO) ***
+dnl *****************************
+AC_ARG_ENABLE(xattr, [  --disable-xattr           build without xattr support])
+msg_xattr=no
+XATTR_LIBS=
+if test "x$enable_xattr" != "xno"; then
+
+dnl either glibc or libattr can provide xattr support
+
+dnl for both of them, we check for getxattr being in
+dnl the library and a valid xattr header.
+
+dnl try glibc
+ AC_CHECK_LIB(c, getxattr,
+   [AC_CHECK_HEADERS(sys/xattr.h,
+     [AC_DEFINE(HAVE_XATTR, 1, [Define to 1 if xattr is available])
+      msg_xattr=yes])
+   ])
+
+  if test "x$msg_xattr" != "xyes"; then
+dnl   failure. try libattr
+   AC_CHECK_LIB(attr, getxattr,
+      [AC_CHECK_HEADERS(attr/xattr.h,
+       [AC_DEFINE(HAVE_XATTR, 1, [Define to 1 if xattr is available])
+        XATTR_LIBS="-lattr"
+        msg_xattr=yes])
+      ])
+  fi
+fi
+AC_SUBST(XATTR_LIBS)
+
+
 dnl ****************************************
 dnl *** platform dependent source checks ***
 dnl ****************************************
@@ -1872,6 +2020,45 @@ int main () {
                        fi
                fi
        fi
+       if test "$ac_cv_header_grp_h" = "yes"; then
+               AC_CACHE_CHECK([for posix getgrgid_r],
+                       ac_cv_func_posix_getgrgid_r,
+                       [AC_TRY_RUN([
+#include <errno.h>
+#include <grp.h>
+int main () { 
+    char buffer[10000];
+    struct group grp, *grpptr = &grp;
+    int error;
+    errno = 0;
+    error = getgrgid_r (0, &grp, buffer, 
+                        sizeof (buffer), &grpptr);
+   return (error < 0 && errno == ENOSYS) 
+          || error == ENOSYS; 
+}                              ],
+                              [ac_cv_func_posix_getgrgid_r=yes],
+                              [ac_cv_func_posix_getgrgid_r=no])])
+               GLIB_ASSERT_SET(ac_cv_func_posix_getgrgid_r)
+               if test "$ac_cv_func_posix_getgrgid_r" = yes; then
+                       AC_DEFINE(HAVE_POSIX_GETGRGID_R,1,
+                               [Have POSIX function getgrgid_r])
+               else
+                       AC_CACHE_CHECK([for nonposix getgrgid_r],
+                               ac_cv_func_nonposix_getgrgid_r,
+                               [AC_TRY_LINK([#include <grp.h>],
+                                               [char buffer[10000];
+                                       struct group grp;       
+                                       getgrgid_r (0, &grp, buffer, 
+                                               sizeof (buffer));],
+                               [ac_cv_func_nonposix_getgrgid_r=yes],
+                               [ac_cv_func_nonposix_getgrgid_r=no])])
+                       GLIB_ASSERT_SET(ac_cv_func_nonposix_getgrgid_r)
+                       if test "$ac_cv_func_nonposix_getgrgid_r" = yes; then
+                               AC_DEFINE(HAVE_NONPOSIX_GETGRGID_R,1,
+                                       [Have non-POSIX function getgrgid_r])
+                       fi
+               fi
+       fi
        LIBS="$G_THREAD_LIBS $LIBS"
        if test x"$have_threads" = xposix; then
                glib_save_CPPFLAGS="$CPPFLAGS"
@@ -2974,6 +3161,10 @@ gthread-2.0.pc
 gthread-2.0-uninstalled.pc
 gobject-2.0.pc
 gobject-2.0-uninstalled.pc
+gio-2.0.pc
+gio-unix-2.0.pc
+gio-2.0-uninstalled.pc
+gio-unix-2.0-uninstalled.pc
 glib-zip
 glib-gettextize
 Makefile
@@ -2992,6 +3183,10 @@ gmodule/gmoduleconf.h
 gobject/Makefile
 gobject/glib-mkenums
 gthread/Makefile
+gio/Makefile
+gio/xdgmime/Makefile
+gio/inotify/Makefile
+gio/fam/Makefile
 po/Makefile.in
 docs/Makefile
 docs/reference/Makefile
@@ -2999,6 +3194,8 @@ docs/reference/glib/Makefile
 docs/reference/glib/version.xml
 docs/reference/gobject/Makefile
 docs/reference/gobject/version.xml
+docs/reference/gio/Makefile
+docs/reference/gio/version.xml
 tests/Makefile
 tests/gobject/Makefile
 tests/refcount/Makefile
index b8dc1ac..9948bd0 100644 (file)
@@ -1,3 +1,13 @@
+2007-11-26  Alexander Larsson  <alexl@redhat.com>
+
+        * Makefile.am:
+        * gio/Makefile.am:
+        * gio/gio-docs.xml:
+        * gio/gio-sections.txt:
+        * gio/gio.types:
+        * gio/version.xml.in:
+       Add gio docs
+
 2007-11-23  Matthias Clasen <mclasen@redhat.com>
 
        * glib/tmpl/i18n.sgml:
index 7f2aab0..63cf90d 100644 (file)
@@ -1,3 +1,3 @@
 include $(top_srcdir)/Makefile.decl
 
-SUBDIRS = glib gobject
+SUBDIRS = glib gobject gio
diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am
new file mode 100644 (file)
index 0000000..85c110b
--- /dev/null
@@ -0,0 +1,90 @@
+include $(top_srcdir)/Makefile.decl
+NULL = 
+
+# The name of the module.
+DOC_MODULE=gio
+
+# The top-level SGML file.
+DOC_MAIN_SGML_FILE=gio-docs.xml
+
+# Extra options to supply to gtkdoc-scan
+SCAN_OPTIONS=--deprecated-guards="G_DISABLE_DEPRECATED"
+
+# The directory containing the source code. Relative to $(srcdir)
+DOC_SOURCE_DIR=$(top_srcdir)/gio
+
+HFILE_GLOB=$(top_srcdir)/gio/*.h
+CFILE_GLOB=$(top_srcdir)/gio/*.c
+
+# Headers to ignore
+IGNORE_HFILES=                            \
+       fam-helper.h                    \
+       gasynchelper.h                  \
+       gdesktopappinfo.h               \
+       gdummyfile.h                    \
+       gfamdirectorymonitor.h          \
+       gfamfilemonitor.h               \
+       ginotifydirectorymonitor.h      \
+       ginotifyfilemonitor.h           \
+       glocaldirectorymonitor.h        \
+       glocalfile.h                    \
+       glocalfileenumerator.h          \
+       glocalfileinfo.h                \
+       glocalfileinputstream.h         \
+       glocalfilemonitor.h             \
+       glocalfileoutputstream.h        \
+       glocalvfs.h                     \
+       gnativevolumemonitor.h          \
+       gpollfilemonitor.h              \
+       gunionvolumemonitor.h           \
+       gunixdrive.h                    \
+       gunixvolume.h                   \
+       gunixvolumemonitor.h            \
+       gvolumeprivate.h                \
+       gwin32appinfo.h                 \
+       inotify-kernel.h                \
+       local_inotify.h                 \
+       local_inotify_syscalls.h        \
+       xdgmime.h                       \
+       xdgmimealias.h                  \
+       xdgmimecache.h                  \
+       xdgmimeglob.h                   \
+       xdgmimeint.h                    \
+       xdgmimemagic.h                  \
+       xdgmimeparent.h                 \
+       $(NULL)
+
+# CFLAGS and LDFLAGS for compiling scan program. Only needed
+# if $(DOC_MODULE).types is non-empty.
+INCLUDES = \
+       -I$(srcdir)                     \
+       -I$(top_srcdir)                 \
+       -I$(top_srcdir)/glib            \
+       -I$(top_srcdir)/gobject         \
+       -I$(top_builddir)               \
+       -I$(top_builddir)/glib          \
+       -I$(top_builddir)/gobject       \
+       $(GLIB_DEBUG_FLAGS)
+
+GTKDOC_LIBS = \
+       $(top_builddir)/glib/libglib-2.0.la             \
+       $(top_builddir)/gobject/libgobject-2.0.la       \
+       $(top_builddir)/gmodule/libgmodule-2.0.la       \
+       $(top_builddir)/gio/libgio-2.0.la               \
+       $(NULL)
+
+# Extra options to supply to gtkdoc-mkdb
+MKDB_OPTIONS = --output-format=xml --sgml-mode
+
+# Images to copy into HTML directory
+HTML_IMAGES =
+
+content_files =                \
+       version.xml
+
+extra_files = version.xml.in
+
+include $(top_srcdir)/gtk-doc.make
+
+EXTRA_DIST +=                          \
+       version.xml.in
diff --git a/docs/reference/gio/gio-docs.xml b/docs/reference/gio/gio-docs.xml
new file mode 100644 (file)
index 0000000..f7c3a8b
--- /dev/null
@@ -0,0 +1,106 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
+               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+<book lang="en" id="gio" xmlns:xi="http://www.w3.org/2003/XInclude">
+<title>GIO Reference Manual</title>
+  <bookinfo>
+    <title>GIO Reference Manual</title>
+    <releaseinfo>for GIO &version;</releaseinfo>
+  </bookinfo>
+  <part>
+  <title>GIO Overview</title>
+  </part>
+  <part>
+  <title>API Reference</title>
+    <chapter id="file_ops">
+       <title>File Operations</title>
+       <xi:include href="xml/gfile.xml"/>
+        <xi:include href="xml/gfileattribute.xml"/>
+       <xi:include href="xml/gfileinfo.xml"/>
+       <xi:include href="xml/gfileenumerator.xml"/>
+       <xi:include href="xml/gmountoperation.xml"/>
+       <xi:include href="xml/gioerror.xml"/>
+    </chapter>
+    <chapter id="file_mon">
+       <title>File System Monitoring</title>
+        <xi:include href="xml/gfilemonitor.xml"/>
+        <xi:include href="xml/gdirectorymonitor.xml"/>
+    </chapter>
+    <chapter id="async">
+        <title>Asynchronous I/O</title>
+       <xi:include href="xml/gcancellable.xml"/>
+       <xi:include href="xml/gasyncresult.xml"/>
+        <xi:include href="xml/gioscheduler.xml"/>
+       <xi:include href="xml/gsimpleasyncresult.xml"/>
+    </chapter>
+    
+    <chapter id="streaming">
+        <title>Streaming I/O</title>
+       <xi:include href="xml/gseekable.xml"/>
+
+        <xi:include href="xml/ginputstream.xml"/>        
+        <xi:include href="xml/goutputstream.xml"/>
+
+        <xi:include href="xml/gfileinputstream.xml"/>
+        <xi:include href="xml/gfileoutputstream.xml"/>
+
+        <xi:include href="xml/gfilterinputstream.xml"/>        
+        <xi:include href="xml/gfilteroutputstream.xml"/>
+       
+        <xi:include href="xml/gmemoryinputstream.xml"/>        
+        <xi:include href="xml/gmemoryoutputstream.xml"/>
+        
+        <xi:include href="xml/gbufferedinputstream.xml"/>
+        <xi:include href="xml/gbufferedoutputstream.xml"/>
+       
+        <xi:include href="xml/gdatainputstream.xml"/>
+        <xi:include href="xml/gdataoutputstream.xml"/>
+        
+        <xi:include href="xml/gsocketinputstream.xml"/>
+        <xi:include href="xml/gsocketoutputstream.xml"/>
+
+    </chapter>
+
+    <chapter id="types">
+        <title>File types and applications</title>
+        <xi:include href="xml/gcontenttype.xml"/>
+       <xi:include href="xml/gappinfo.xml"/>
+    </chapter>
+    
+    <chapter id="volume_mon">
+       <title>Volumes and Drives</title>
+        <xi:include href="xml/gvolumemonitor.xml"/>
+       <xi:include href="xml/gvolume.xml"/>
+       <xi:include href="xml/gdrive.xml"/>
+       <xi:include href="xml/gunixmounts.xml"/>
+    </chapter>
+    <chapter id="icons">
+       <title>Icons</title>
+       <xi:include href="xml/gicon.xml"/>
+       <xi:include href="xml/gfileicon.xml"/>
+       <xi:include href="xml/gloadableicon.xml"/>
+       <xi:include href="xml/gthemedicon.xml"/>
+    </chapter>
+    <chapter id="utils">   
+       <title>Utilities</title>
+        <xi:include href="xml/gfilenamecompleter.xml"/>
+        <xi:include href="xml/gurifuncs.xml"/>
+    </chapter>
+    <chapter id="extending">
+       <title>Extending GIO</title>
+        <xi:include href="xml/gvfs.xml"/>
+       <xi:include href="xml/giomodule.xml"/>
+    </chapter>
+  </part>
+
+  <chapter id="gio-hierarchy">
+    <title>Object Hierarchy</title>
+      <xi:include href="xml/tree_index.sgml"/>
+  </chapter>
+  
+  <index>
+    <title id="index-all">Index</title>
+  </index>
+</book>
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
new file mode 100644 (file)
index 0000000..f3f85b5
--- /dev/null
@@ -0,0 +1,1159 @@
+<SECTION>
+<FILE>gvfs</FILE>
+<TITLE>GVfs</TITLE>
+GVfs
+g_vfs_get_name
+g_vfs_get_priority
+g_vfs_get_file_for_path
+g_vfs_get_file_for_uri
+g_vfs_parse_name
+g_vfs_get_default
+g_vfs_get_local
+g_vfs_is_active
+<SUBSECTION Standard>
+GVfsClass
+G_VFS
+G_IS_VFS
+G_TYPE_VFS
+G_VFS_CLASS
+G_IS_VFS_CLASS
+G_VFS_GET_CLASS
+<SUBSECTION Private>
+g_vfs_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gfile</FILE>
+<TITLE>GFile</TITLE>
+GFile
+GFileIface
+GFileQueryInfoFlags
+GFileCreateFlags
+GFileCopyFlags
+GFileMonitorFlags
+GFileProgressCallback
+GFileReadMoreCallback
+g_file_new_for_path
+g_file_new_for_uri
+g_file_new_for_commandline_arg
+g_file_parse_name
+g_file_dup
+g_file_hash
+g_file_equal
+g_file_get_basename
+g_file_get_path
+g_file_get_uri
+g_file_get_parse_name
+g_file_get_parent
+g_file_get_child
+g_file_get_child_for_display_name
+g_file_contains_file
+g_file_get_relative_path
+g_file_resolve_relative_path
+g_file_is_native
+g_file_has_uri_scheme
+g_file_get_uri_scheme
+g_file_read
+g_file_read_async
+g_file_read_finish
+g_file_append_to
+g_file_create
+g_file_replace
+g_file_append_to_async
+g_file_append_to_finish
+g_file_create_async
+g_file_create_finish
+g_file_replace_async
+g_file_replace_finish
+g_file_query_info
+g_file_query_info_async
+g_file_query_info_finish
+g_file_query_filesystem_info
+g_file_find_enclosing_volume
+g_file_enumerate_children
+g_file_enumerate_children_async
+g_file_enumerate_children_finish
+g_file_set_display_name
+g_file_set_display_name_async
+g_file_set_display_name_finish
+g_file_delete
+g_file_trash
+g_file_copy
+g_file_move
+g_file_make_directory
+g_file_make_symbolic_link
+g_file_query_settable_attributes
+g_file_query_writable_namespaces
+g_file_set_attribute
+g_file_set_attributes_from_info
+g_file_set_attributes_async
+g_file_set_attributes_finish
+g_file_set_attribute_string
+g_file_set_attribute_byte_string
+g_file_set_attribute_uint32
+g_file_set_attribute_int32
+g_file_set_attribute_uint64
+g_file_set_attribute_int64
+g_mount_for_location
+g_mount_for_location_finish
+g_file_mount_mountable
+g_file_mount_mountable_finish
+g_file_unmount_mountable
+g_file_unmount_mountable_finish
+g_file_eject_mountable
+g_file_eject_mountable_finish
+g_file_monitor_directory
+g_file_monitor_file
+g_file_load_contents
+g_file_load_contents_async
+g_file_load_contents_finish
+g_file_load_partial_contents_async
+g_file_load_partial_contents_finish
+g_file_replace_contents
+g_file_replace_contents_async
+g_file_replace_contents_finish
+g_file_copy_attributes
+<SUBSECTION Standard>
+G_FILE
+G_IS_FILE
+G_TYPE_FILE
+G_FILE_GET_IFACE
+<SUBSECTION Private>
+g_file_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gfileenumerator</FILE>
+<TITLE>GFileEnumerator</TITLE>
+GFileEnumerator
+g_file_enumerator_next_file
+g_file_enumerator_close
+g_file_enumerator_next_files_async
+g_file_enumerator_next_files_finish
+g_file_enumerator_close_async
+g_file_enumerator_close_finish
+g_file_enumerator_is_closed
+g_file_enumerator_has_pending
+g_file_enumerator_set_pending
+<SUBSECTION Standard>
+GFileEnumeratorClass
+G_FILE_ENUMERATOR
+G_IS_FILE_ENUMERATOR
+G_TYPE_FILE_ENUMERATOR
+G_FILE_ENUMERATOR_CLASS
+G_IS_FILE_ENUMERATOR_CLASS
+G_FILE_ENUMERATOR_GET_CLASS
+<SUBSECTION Private>
+g_file_enumerator_get_type
+GFileEnumeratorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gfileinfo</FILE>
+<TITLE>GFileInfo</TITLE>
+GFileAttributeMatcher
+GFileType
+GFileInfo
+G_FILE_ATTRIBUTE_STD_TYPE
+G_FILE_ATTRIBUTE_STD_IS_HIDDEN
+G_FILE_ATTRIBUTE_STD_IS_BACKUP
+G_FILE_ATTRIBUTE_STD_IS_SYMLINK
+G_FILE_ATTRIBUTE_STD_IS_VIRTUAL
+G_FILE_ATTRIBUTE_STD_NAME
+G_FILE_ATTRIBUTE_STD_DISPLAY_NAME
+G_FILE_ATTRIBUTE_STD_EDIT_NAME
+G_FILE_ATTRIBUTE_STD_COPY_NAME
+G_FILE_ATTRIBUTE_STD_ICON
+G_FILE_ATTRIBUTE_STD_CONTENT_TYPE
+G_FILE_ATTRIBUTE_STD_FAST_CONTENT_TYPE
+G_FILE_ATTRIBUTE_STD_SIZE
+G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET
+G_FILE_ATTRIBUTE_STD_TARGET_URI
+G_FILE_ATTRIBUTE_STD_SORT_ORDER
+G_FILE_ATTRIBUTE_ETAG_VALUE
+G_FILE_ATTRIBUTE_ID_FILE
+G_FILE_ATTRIBUTE_ID_FS
+G_FILE_ATTRIBUTE_ACCESS_CAN_READ
+G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE
+G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE
+G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE
+G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH
+G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME
+G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT
+G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT
+G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT
+G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE
+G_FILE_ATTRIBUTE_MOUNTABLE_HAL_UDI
+G_FILE_ATTRIBUTE_TIME_MODIFIED
+G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC
+G_FILE_ATTRIBUTE_TIME_ACCESS
+G_FILE_ATTRIBUTE_TIME_ACCESS_USEC
+G_FILE_ATTRIBUTE_TIME_CHANGED
+G_FILE_ATTRIBUTE_TIME_CHANGED_USEC
+G_FILE_ATTRIBUTE_TIME_CREATED
+G_FILE_ATTRIBUTE_TIME_CREATED_USEC
+G_FILE_ATTRIBUTE_UNIX_DEVICE
+G_FILE_ATTRIBUTE_UNIX_INODE
+G_FILE_ATTRIBUTE_UNIX_MODE
+G_FILE_ATTRIBUTE_UNIX_NLINK
+G_FILE_ATTRIBUTE_UNIX_UID
+G_FILE_ATTRIBUTE_UNIX_GID
+G_FILE_ATTRIBUTE_UNIX_RDEV
+G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE
+G_FILE_ATTRIBUTE_UNIX_BLOCKS
+G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT
+G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE
+G_FILE_ATTRIBUTE_DOS_IS_SYSTEM
+G_FILE_ATTRIBUTE_OWNER_USER
+G_FILE_ATTRIBUTE_OWNER_USER_REAL
+G_FILE_ATTRIBUTE_OWNER_GROUP
+G_FILE_ATTRIBUTE_THUMBNAIL_PATH
+G_FILE_ATTRIBUTE_THUMBNAILING_FAILED
+G_FILE_ATTRIBUTE_FS_SIZE
+G_FILE_ATTRIBUTE_FS_FREE
+G_FILE_ATTRIBUTE_FS_TYPE
+G_FILE_ATTRIBUTE_FS_READONLY
+G_FILE_ATTRIBUTE_GVFS_BACKEND
+g_file_info_new
+g_file_info_dup
+g_file_info_copy_into
+g_file_info_has_attribute
+g_file_info_list_attributes
+g_file_info_get_attribute_type
+g_file_info_remove_attribute
+g_file_info_get_attribute
+g_file_info_get_attribute_string
+g_file_info_get_attribute_byte_string
+g_file_info_get_attribute_boolean
+g_file_info_get_attribute_uint32
+g_file_info_get_attribute_int32
+g_file_info_get_attribute_uint64
+g_file_info_get_attribute_int64
+g_file_info_get_attribute_object
+g_file_info_set_attribute
+g_file_info_set_attribute_string
+g_file_info_set_attribute_byte_string
+g_file_info_set_attribute_boolean
+g_file_info_set_attribute_uint32
+g_file_info_set_attribute_int32
+g_file_info_set_attribute_uint64
+g_file_info_set_attribute_int64
+g_file_info_set_attribute_object
+g_file_info_clear_status
+g_file_info_get_file_type
+g_file_info_get_is_hidden
+g_file_info_get_is_backup
+g_file_info_get_is_symlink
+g_file_info_get_name
+g_file_info_get_display_name
+g_file_info_get_edit_name
+g_file_info_get_icon
+g_file_info_get_content_type
+g_file_info_get_size
+g_file_info_get_modification_time
+g_file_info_get_symlink_target
+g_file_info_get_etag
+g_file_info_get_sort_order
+g_file_info_set_attribute_mask
+g_file_info_unset_attribute_mask
+g_file_info_set_file_type
+g_file_info_set_is_hidden
+g_file_info_set_is_symlink
+g_file_info_set_name
+g_file_info_set_display_name
+g_file_info_set_edit_name
+g_file_info_set_icon
+g_file_info_set_content_type
+g_file_info_set_size
+g_file_info_set_modification_time
+g_file_info_set_symlink_target
+g_file_info_set_sort_order
+g_format_file_size_for_display
+g_file_attribute_matcher_new
+g_file_attribute_matcher_ref
+g_file_attribute_matcher_unref
+g_file_attribute_matcher_matches
+g_file_attribute_matcher_matches_only
+g_file_attribute_matcher_enumerate_namespace
+g_file_attribute_matcher_enumerate_next
+<SUBSECTION Standard>
+GFileInfoClass
+G_FILE_INFO
+G_IS_FILE_INFO
+G_TYPE_FILE_INFO
+G_FILE_INFO_CLASS
+G_IS_FILE_INFO_CLASS
+G_FILE_INFO_GET_CLASS
+<SUBSECTION Private>
+g_file_info_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gfileattribute</FILE>
+<TITLE>GFileAttribute</TITLE>
+GFileAttributeType
+GFileAttributeFlags
+GFileAttributeStatus
+G_FILE_ATTRIBUTE_VALUE_INIT
+GFileAttributeValue
+GFileAttributeInfo
+GFileAttributeInfoList
+g_file_attribute_value_new
+g_file_attribute_value_free
+g_file_attribute_value_clear
+g_file_attribute_value_set
+g_file_attribute_value_dup
+g_file_attribute_value_as_string
+g_file_attribute_value_get_string
+g_file_attribute_value_get_byte_string
+g_file_attribute_value_get_boolean
+g_file_attribute_value_get_uint32
+g_file_attribute_value_get_int32
+g_file_attribute_value_get_uint64
+g_file_attribute_value_get_int64
+g_file_attribute_value_get_object
+g_file_attribute_value_set_string
+g_file_attribute_value_set_byte_string
+g_file_attribute_value_set_boolean
+g_file_attribute_value_set_uint32
+g_file_attribute_value_set_int32
+g_file_attribute_value_set_uint64
+g_file_attribute_value_set_int64
+g_file_attribute_value_set_object
+g_file_attribute_info_list_new
+g_file_attribute_info_list_ref
+g_file_attribute_info_list_unref
+g_file_attribute_info_list_dup
+g_file_attribute_info_list_lookup
+g_file_attribute_info_list_add
+</SECTION>
+
+<SECTION>
+<FILE>gfilemonitor</FILE>
+<TITLE>GFileMonitor</TITLE>
+GFileMonitorEvent
+GFileMonitor
+g_file_monitor_cancel
+g_file_monitor_is_cancelled
+g_file_monitor_set_rate_limit
+g_file_monitor_emit_event
+<SUBSECTION Standard>
+GFileMonitorClass
+G_FILE_MONITOR
+G_IS_FILE_MONITOR
+G_TYPE_FILE_MONITOR
+G_FILE_MONITOR_CLASS
+G_IS_FILE_MONITOR_CLASS
+G_FILE_MONITOR_GET_CLASS
+<SUBSECTION Private>
+g_file_monitor_get_type
+GFileMonitorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdirectorymonitor</FILE>
+<TITLE>GDirectoryMonitor</TITLE>
+GDirectoryMonitor
+g_directory_monitor_cancel
+g_directory_monitor_is_cancelled
+g_directory_monitor_set_rate_limit
+g_directory_monitor_emit_event
+<SUBSECTION Standard>
+GDirectoryMonitorClass
+G_DIRECTORY_MONITOR
+G_IS_DIRECTORY_MONITOR
+G_TYPE_DIRECTORY_MONITOR
+G_DIRECTORY_MONITOR_CLASS
+G_IS_DIRECTORY_MONITOR_CLASS
+G_DIRECTORY_MONITOR_GET_CLASS
+<SUBSECTION Private>
+g_directory_monitor_get_type
+GDirectoryMonitorPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gicon</FILE>
+<TITLE>GIcon</TITLE>
+GIcon
+GIconIface
+g_icon_hash
+g_icon_equal
+<SUBSECTION Standard>
+G_ICON
+G_IS_ICON
+G_TYPE_ICON
+G_ICON_GET_IFACE
+<SUBSECTION Private>
+g_icon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gthemedicon</FILE>
+<TITLE>GThemedIcon</TITLE>
+GThemedIcon
+g_themed_icon_new
+g_themed_icon_new_from_names
+<SUBSECTION Standard>
+GThemedIconClass
+G_THEMED_ICON
+G_IS_THEMED_ICON
+G_TYPE_THEMED_ICON
+G_THEMED_ICON_CLASS
+G_IS_THEMED_ICON_CLASS
+G_THEMED_ICON_GET_CLASS
+<SUBSECTION Private>
+g_themed_icon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gloadableicon</FILE>
+<TITLE>GLoadableIcon</TITLE>
+GLoadableIcon
+GLoadableIconIface
+g_loadable_icon_load
+g_loadable_icon_load_async
+g_loadable_icon_load_finish
+<SUBSECTION Standard>
+G_LOADABLE_ICON
+G_IS_LOADABLE_ICON
+G_TYPE_LOADABLE_ICON
+G_LOADABLE_ICON_GET_IFACE
+<SUBSECTION Private>
+g_loadable_icon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gfileicon</FILE>
+<TITLE>GFileIcon</TITLE>
+GFileIcon
+g_file_icon_new
+g_file_icon_get_file
+<SUBSECTION Standard>
+GFileIconClass
+G_FILE_ICON
+G_IS_FILE_ICON
+G_TYPE_FILE_ICON
+G_FILE_ICON_CLASS
+G_IS_FILE_ICON_CLASS
+G_FILE_ICON_GET_CLASS
+<SUBSECTION Private>
+g_file_icon_get_type
+</SECTION>
+
+<SECTION>
+<FILE>ginputstream</FILE>
+<TITLE>GInputStream</TITLE>
+GInputStream
+g_input_stream_read
+g_input_stream_read_all
+g_input_stream_skip
+g_input_stream_close
+g_input_stream_read_async
+g_input_stream_read_finish
+g_input_stream_skip_async
+g_input_stream_skip_finish
+g_input_stream_close_async
+g_input_stream_close_finish
+g_input_stream_is_closed
+g_input_stream_has_pending
+g_input_stream_set_pending
+<SUBSECTION Standard>
+GInputStreamClass
+G_INPUT_STREAM
+G_IS_INPUT_STREAM
+G_TYPE_INPUT_STREAM
+G_INPUT_STREAM_CLASS
+G_IS_INPUT_STREAM_CLASS
+G_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_input_stream_get_type
+GInputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gfileinputstream</FILE>
+<TITLE>GFileInputStream</TITLE>
+GFileInputStream
+g_file_input_stream_query_info
+g_file_input_stream_query_info_async
+g_file_input_stream_query_info_finish
+g_file_input_stream_tell
+g_file_input_stream_can_seek
+g_file_input_stream_seek
+<SUBSECTION Standard>
+GFileInputStreamClass
+G_FILE_INPUT_STREAM
+G_IS_FILE_INPUT_STREAM
+G_TYPE_FILE_INPUT_STREAM
+G_FILE_INPUT_STREAM_CLASS
+G_IS_FILE_INPUT_STREAM_CLASS
+G_FILE_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_file_input_stream_get_type
+GFileInputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gfilterinputstream</FILE>
+<TITLE>GFilterInputStream</TITLE>
+GFilterInputStream
+g_filter_input_stream_get_base_stream
+<SUBSECTION Standard>
+GFilterInputStreamClass
+G_FILTER_INPUT_STREAM
+G_IS_FILTER_INPUT_STREAM
+G_TYPE_FILTER_INPUT_STREAM
+G_FILTER_INPUT_STREAM_CLASS
+G_IS_FILTER_INPUT_STREAM_CLASS
+G_FILTER_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_filter_input_stream_get_type
+GFilterInputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gsocketinputstream</FILE>
+<TITLE>GSocketInputStream</TITLE>
+GSocketInputStream
+g_socket_input_stream_new
+<SUBSECTION Standard>
+GSocketInputStreamClass
+G_SOCKET_INPUT_STREAM
+G_IS_SOCKET_INPUT_STREAM
+G_TYPE_SOCKET_INPUT_STREAM
+G_SOCKET_INPUT_STREAM_CLASS
+G_IS_SOCKET_INPUT_STREAM_CLASS
+G_SOCKET_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_socket_input_stream_get_type
+GSocketInputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gmemoryinputstream</FILE>
+<TITLE>GMemoryInputStream</TITLE>
+GMemoryInputStream
+g_memory_input_stream_from_data
+g_memory_input_stream_set_free_data
+g_memory_input_stream_get_data
+g_memory_input_stream_get_data_size
+<SUBSECTION Standard>
+GMemoryInputStreamClass
+G_MEMORY_INPUT_STREAM
+G_IS_MEMORY_INPUT_STREAM
+G_TYPE_MEMORY_INPUT_STREAM
+G_MEMORY_INPUT_STREAM_CLASS
+G_IS_MEMORY_INPUT_STREAM_CLASS
+G_MEMORY_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+GMemoryInputStreamPrivate
+g_memory_input_stream_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gdatainputstream</FILE>
+<TITLE>GDataInputStream</TITLE>
+GDataInputStream
+GDataStreamByteOrder
+GDataStreamNewlineType
+g_data_input_stream_new
+g_data_input_stream_set_byte_order
+g_data_input_stream_get_byte_order
+g_data_input_stream_set_newline_type
+g_data_input_stream_get_newline_type
+g_data_input_stream_read_byte
+g_data_input_stream_read_int16
+g_data_input_stream_read_uint16
+g_data_input_stream_read_int32
+g_data_input_stream_read_uint32
+g_data_input_stream_read_int64
+g_data_input_stream_read_uint64
+g_data_input_stream_read_line
+g_data_input_stream_read_until
+<SUBSECTION Standard>
+GDataInputStreamClass
+G_DATA_INPUT_STREAM
+G_IS_DATA_INPUT_STREAM
+G_TYPE_DATA_INPUT_STREAM
+G_DATA_INPUT_STREAM_CLASS
+G_IS_DATA_INPUT_STREAM_CLASS
+G_DATA_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_data_input_stream_get_type
+GDataInputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gbufferedinputstream</FILE>
+<TITLE>GBufferedInputStream</TITLE>
+GBufferedInputStream
+g_buffered_input_stream_new
+g_buffered_input_stream_new_sized
+g_buffered_input_stream_get_buffer_size
+g_buffered_input_stream_set_buffer_size
+g_buffered_input_stream_get_available
+g_buffered_input_stream_peek
+g_buffered_input_stream_fill
+g_buffered_input_stream_fill_async
+g_buffered_input_stream_fill_finish
+<SUBSECTION Standard>
+GBufferedInputStreamClass
+G_BUFFERED_INPUT_STREAM
+G_IS_BUFFERED_INPUT_STREAM
+G_TYPE_BUFFERED_INPUT_STREAM
+G_BUFFERED_INPUT_STREAM_CLASS
+G_IS_BUFFERED_INPUT_STREAM_CLASS
+G_BUFFERED_INPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_buffered_input_stream_get_type
+GBufferedInputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>goutputstream</FILE>
+<TITLE>GOutputStream</TITLE>
+GOutputStreamSpliceFlags
+GOutputStream
+g_output_stream_write
+g_output_stream_write_all
+g_output_stream_splice
+g_output_stream_flush
+g_output_stream_close
+g_output_stream_write_async
+g_output_stream_write_finish
+g_output_stream_splice_async
+g_output_stream_splice_finish
+g_output_stream_flush_async
+g_output_stream_flush_finish
+g_output_stream_close_async
+g_output_stream_close_finish
+g_output_stream_is_closed
+g_output_stream_has_pending
+g_output_stream_set_pending
+<SUBSECTION Standard>
+GOutputStreamClass
+G_OUTPUT_STREAM
+G_IS_OUTPUT_STREAM
+G_TYPE_OUTPUT_STREAM
+G_OUTPUT_STREAM_CLASS
+G_IS_OUTPUT_STREAM_CLASS
+G_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_output_stream_get_type
+GOutputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gfileoutputstream</FILE>
+<TITLE>GFileOutputStream</TITLE>
+GFileOutputStream
+g_file_output_stream_query_info
+g_file_output_stream_query_info_async
+g_file_output_stream_query_info_finish
+g_file_output_stream_get_etag
+g_file_output_stream_tell
+g_file_output_stream_can_seek
+g_file_output_stream_seek
+g_file_output_stream_can_truncate
+g_file_output_stream_truncate
+<SUBSECTION Standard>
+GFileOutputStreamClass
+G_FILE_OUTPUT_STREAM
+G_IS_FILE_OUTPUT_STREAM
+G_TYPE_FILE_OUTPUT_STREAM
+G_FILE_OUTPUT_STREAM_CLASS
+G_IS_FILE_OUTPUT_STREAM_CLASS
+G_FILE_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_file_output_stream_get_type
+GFileOutputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gfilteroutputstream</FILE>
+<TITLE>GFilterOutputStream</TITLE>
+GFilterOutputStream
+g_filter_output_stream_get_base_stream
+<SUBSECTION Standard>
+GFilterOutputStreamClass
+G_FILTER_OUTPUT_STREAM
+G_IS_FILTER_OUTPUT_STREAM
+G_TYPE_FILTER_OUTPUT_STREAM
+G_FILTER_OUTPUT_STREAM_CLASS
+G_IS_FILTER_OUTPUT_STREAM_CLASS
+G_FILTER_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_filter_output_stream_get_type
+GFilterOutputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gbufferedoutputstream</FILE>
+<TITLE>GBufferedOutputStream</TITLE>
+GBufferedOutputStream
+g_buffered_output_stream_new
+g_buffered_output_stream_new_sized
+g_buffered_output_stream_get_buffer_size
+g_buffered_output_stream_set_buffer_size
+g_buffered_output_stream_get_auto_grow
+g_buffered_output_stream_set_auto_grow
+<SUBSECTION Standard>
+GBufferedOutputStreamClass
+G_BUFFERED_OUTPUT_STREAM
+G_IS_BUFFERED_OUTPUT_STREAM
+G_TYPE_BUFFERED_OUTPUT_STREAM
+G_BUFFERED_OUTPUT_STREAM_CLASS
+G_IS_BUFFERED_OUTPUT_STREAM_CLASS
+G_BUFFERED_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_buffered_output_stream_get_type
+GBufferedOutputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gmemoryoutputstream</FILE>
+<TITLE>GMemoryOutputStream</TITLE>
+GMemoryOutputStream
+g_memory_output_stream_new
+g_memory_output_stream_set_max_size
+g_memory_output_stream_get_data
+g_memory_output_stream_set_free_data
+<SUBSECTION Standard>
+GMemoryOutputStreamClass
+G_MEMORY_OUTPUT_STREAM
+G_IS_MEMORY_OUTPUT_STREAM
+G_TYPE_MEMORY_OUTPUT_STREAM
+G_MEMORY_OUTPUT_STREAM_CLASS
+G_IS_MEMORY_OUTPUT_STREAM_CLASS
+G_MEMORY_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_memory_output_stream_get_type
+GMemoryOutputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gdataoutputstream</FILE>
+<TITLE>GDataOutputStream</TITLE>
+GDataOutputStream
+GDataOutputStreamClass
+g_data_output_stream_new
+g_data_output_stream_set_byte_order
+g_data_output_stream_get_byte_order
+g_data_output_stream_set_expand_buffer
+g_data_output_stream_put_byte
+g_data_output_stream_put_int16
+g_data_output_stream_put_uint16
+g_data_output_stream_put_int32
+g_data_output_stream_put_uint32
+g_data_output_stream_put_int64
+g_data_output_stream_put_uint64
+g_data_output_stream_put_string
+<SUBSECTION Standard>
+G_DATA_OUTPUT_STREAM
+G_IS_DATA_OUTPUT_STREAM
+G_TYPE_DATA_OUTPUT_STREAM
+G_DATA_OUTPUT_STREAM_CLASS
+G_IS_DATA_OUTPUT_STREAM_CLASS
+G_DATA_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_data_output_stream_get_type
+GDataOutputStreamPrivate
+
+</SECTION>
+
+<SECTION>
+<FILE>gsocketoutputstream</FILE>
+<TITLE>GSocketOutputStream</TITLE>
+GSocketOutputStream
+g_socket_output_stream_new
+<SUBSECTION Standard>
+GSocketOutputStreamClass
+G_SOCKET_OUTPUT_STREAM
+G_IS_SOCKET_OUTPUT_STREAM
+G_TYPE_SOCKET_OUTPUT_STREAM
+G_SOCKET_OUTPUT_STREAM_CLASS
+G_IS_SOCKET_OUTPUT_STREAM_CLASS
+G_SOCKET_OUTPUT_STREAM_GET_CLASS
+<SUBSECTION Private>
+g_socket_output_stream_get_type
+GSocketOutputStreamPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gseekable</FILE>
+<TITLE>GSeekable</TITLE>
+GSeekable
+GSeekableIface
+g_seekable_tell
+g_seekable_can_seek
+g_seekable_seek
+g_seekable_can_truncate
+g_seekable_truncate
+<SUBSECTION Standard>
+G_SEEKABLE
+G_IS_SEEKABLE
+G_TYPE_SEEKABLE
+G_SEEKABLE_GET_IFACE
+<SUBSECTION Private>
+g_seekable_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gvolumemonitor</FILE>
+<TITLE>GVolumeMonitor</TITLE>
+GVolumeMonitor
+g_volume_monitor_get
+g_volume_monitor_get_mounted_volumes
+g_volume_monitor_get_connected_drives
+<SUBSECTION Standard>
+GVolumeMonitorClass
+G_VOLUME_MONITOR
+G_IS_VOLUME_MONITOR
+G_TYPE_VOLUME_MONITOR
+G_VOLUME_MONITOR_CLASS
+G_IS_VOLUME_MONITOR_CLASS
+G_VOLUME_MONITOR_GET_CLASS
+<SUBSECTION Private>
+g_volume_monitor_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gvolume</FILE>
+<TITLE>GVolume</TITLE>
+GVolume
+GVolumeIface
+g_volume_get_root
+g_volume_get_name
+g_volume_get_icon
+g_volume_get_drive
+g_volume_can_unmount
+g_volume_can_eject
+g_volume_unmount
+g_volume_unmount_finish
+g_volume_eject
+g_volume_eject_finish
+<SUBSECTION Standard>
+G_VOLUME
+G_IS_VOLUME
+G_TYPE_VOLUME
+G_VOLUME_GET_IFACE
+<SUBSECTION Private>
+g_volume_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gdrive</FILE>
+<TITLE>GDrive</TITLE>
+GDrive
+GDriveIface
+g_drive_get_name
+g_drive_get_icon
+g_drive_has_volumes
+g_drive_get_volumes
+g_drive_is_automounted
+g_drive_can_mount
+g_drive_can_eject
+g_drive_mount
+g_drive_mount_finish
+g_drive_eject
+g_drive_eject_finish
+<SUBSECTION Standard>
+G_DRIVE
+G_IS_DRIVE
+G_TYPE_DRIVE
+G_DRIVE_GET_IFACE
+<SUBSECTION Private>
+g_drive_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gcancellable</FILE>
+<TITLE>GCancellable</TITLE>
+GCancellable
+g_cancellable_new
+g_cancellable_is_cancelled
+g_cancellable_set_error_if_cancelled
+g_cancellable_get_fd
+g_cancellable_get_current
+g_push_current_cancellable
+g_pop_current_cancellable
+g_cancellable_reset
+g_cancellable_cancel
+<SUBSECTION Standard>
+GCancellableClass
+G_CANCELLABLE
+G_IS_CANCELLABLE
+G_TYPE_CANCELLABLE
+G_CANCELLABLE_CLASS
+G_IS_CANCELLABLE_CLASS
+G_CANCELLABLE_GET_CLASS
+<SUBSECTION Private>
+g_cancellable_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gasyncresult</FILE>
+<TITLE>GAsyncResult</TITLE>
+GAsyncResult
+GAsyncResultIface
+GAsyncReadyCallback
+g_async_result_get_user_data
+g_async_result_get_source_object
+<SUBSECTION Standard>
+G_ASYNC_RESULT
+G_IS_ASYNC_RESULT
+G_TYPE_ASYNC_RESULT
+G_ASYNC_RESULT_GET_IFACE
+<SUBSECTION Private>
+g_async_result_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gsimpleasyncresult</FILE>
+<TITLE>GSimpleAsyncResult</TITLE>
+GSimpleAsyncResult
+GSimpleAsyncThreadFunc
+g_simple_async_result_new
+g_simple_async_result_new_error
+g_simple_async_result_new_from_error
+g_simple_async_result_set_op_res_gpointer
+g_simple_async_result_get_op_res_gpointer
+g_simple_async_result_set_op_res_gssize
+g_simple_async_result_get_op_res_gssize
+g_simple_async_result_set_op_res_gboolean
+g_simple_async_result_get_op_res_gboolean
+g_simple_async_result_get_source_tag
+g_simple_async_result_set_handle_cancellation
+g_simple_async_result_complete
+g_simple_async_result_complete_in_idle
+g_simple_async_result_run_in_thread
+g_simple_async_result_set_from_error
+g_simple_async_result_propagate_error
+g_simple_async_result_set_error
+g_simple_async_result_set_error_va
+g_simple_async_report_error_in_idle
+<SUBSECTION Standard>
+GSimpleAsyncResultClass
+G_SIMPLE_ASYNC_RESULT
+G_IS_SIMPLE_ASYNC_RESULT
+G_TYPE_SIMPLE_ASYNC_RESULT
+G_SIMPLE_ASYNC_RESULT_CLASS
+G_IS_SIMPLE_ASYNC_RESULT_CLASS
+G_SIMPLE_ASYNC_RESULT_GET_CLASS
+<SUBSECTION Private>
+g_simple_async_result_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gioscheduler</FILE>
+<TITLE>GIOScheduler</TITLE>
+GIOJob
+GIOJobFunc
+GIODataFunc
+g_schedule_io_job
+g_cancel_all_io_jobs
+g_io_job_send_to_mainloop
+</SECTION>
+
+<SECTION>
+<FILE>gioerror</FILE>
+<TITLE>GIOError</TITLE>
+G_IO_ERROR
+GIOErrorEnum
+g_io_error_from_errno
+<SUBSECTION Private>
+g_io_error_quark
+</SECTION>
+
+<SECTION>
+<FILE>gcontenttype</FILE>
+<TITLE>GContentType</TITLE>
+g_content_type_equals
+g_content_type_is_a
+g_content_type_is_unknown
+g_content_type_get_description
+g_content_type_get_mime_type
+g_content_type_get_icon
+g_content_type_can_be_executable
+g_content_type_guess
+g_content_types_get_registered
+</SECTION>
+
+<SECTION>
+<FILE>gappinfo</FILE>
+<TITLE>GAppInfo</TITLE>
+GAppInfoCreateFlags
+GAppInfo
+GAppLaunchContext
+g_app_info_create_from_commandline
+g_app_info_dup
+g_app_info_equal
+g_app_info_get_id
+g_app_info_get_name
+g_app_info_get_description
+g_app_info_get_executable
+g_app_info_get_icon
+g_app_info_launch
+g_app_info_supports_uris
+g_app_info_launch_uris
+g_app_info_should_show
+g_app_info_set_as_default_for_type
+g_app_info_set_as_default_for_extension
+g_app_info_add_supports_type
+g_app_info_can_remove_supports_type
+g_app_info_remove_supports_type
+g_app_info_get_all
+g_app_info_get_all_for_type
+g_app_info_get_default_for_type
+g_app_info_get_default_for_uri_scheme
+g_app_launch_context_get_display
+g_app_launch_context_get_startup_notify_id
+g_app_launch_context_get_type
+g_app_launch_context_launch_failed
+g_app_launch_context_new
+<SUBSECTION Standard>
+GAppInfoIface
+GAppLaunchContextClass
+G_APP_INFO
+G_IS_APP_INFO
+G_TYPE_APP_INFO
+G_APP_INFO_GET_IFACE
+G_APP_LAUNCH_CONTEXT
+G_APP_LAUNCH_CONTEXT_CLASS
+G_APP_LAUNCH_CONTEXT_GET_CLASS
+G_IS_APP_LAUNCH_CONTEXT
+G_IS_APP_LAUNCH_CONTEXT_CLASS
+G_TYPE_APP_LAUNCH_CONTEXT
+<SUBSECTION Private>
+g_app_info_get_type
+GAppLaunchContextPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gmountoperation</FILE>
+<TITLE>GMountOperation</TITLE>
+GPasswordFlags
+GPasswordSave
+GMountOperation
+g_mount_operation_new
+g_mount_operation_get_username
+g_mount_operation_set_username
+g_mount_operation_get_password
+g_mount_operation_set_password
+g_mount_operation_get_anonymous
+g_mount_operation_set_anonymous
+g_mount_operation_get_domain
+g_mount_operation_set_domain
+g_mount_operation_get_password_save
+g_mount_operation_set_password_save
+g_mount_operation_get_choice
+g_mount_operation_set_choice
+g_mount_operation_reply
+<SUBSECTION Standard>
+GMountOperationClass
+G_MOUNT_OPERATION
+G_IS_MOUNT_OPERATION
+G_TYPE_MOUNT_OPERATION
+G_MOUNT_OPERATION_CLASS
+G_IS_MOUNT_OPERATION_CLASS
+G_MOUNT_OPERATION_GET_CLASS
+<SUBSECTION Private>
+g_mount_operation_get_type
+GMountOperationPrivate
+</SECTION>
+
+<SECTION>
+<FILE>gfilenamecompleter</FILE>
+<TITLE>GFilenameCompleter</TITLE>
+GFilenameCompleter
+g_filename_completer_new
+g_filename_completer_get_completion_suffix
+g_filename_completer_get_completions
+g_filename_completer_set_dirs_only
+<SUBSECTION Standard>
+GFilenameCompleterClass
+G_FILENAME_COMPLETER
+G_IS_FILENAME_COMPLETER
+G_TYPE_FILENAME_COMPLETER
+G_FILENAME_COMPLETER_CLASS
+G_IS_FILENAME_COMPLETER_CLASS
+G_FILENAME_COMPLETER_GET_CLASS
+<SUBSECTION Private>
+g_filename_completer_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gunixmounts</FILE>
+<TITLE>Unix Mounts</TITLE>
+GUnixMount
+GUnixMountPoint
+GUnixMountType
+GUnixMountMonitor
+g_unix_mount_free
+g_unix_mount_point_free
+g_unix_mount_compare
+g_unix_mount_get_mount_path
+g_unix_mount_get_device_path
+g_unix_mount_get_fs_type
+g_unix_mount_is_readonly
+g_unix_mount_is_system_internal
+g_unix_mount_guess_type
+g_unix_mount_point_compare
+g_unix_mount_point_get_mount_path
+g_unix_mount_point_get_device_path
+g_unix_mount_point_get_fs_type
+g_unix_mount_point_is_readonly
+g_unix_mount_point_is_user_mountable
+g_unix_mount_point_is_loopback
+g_unix_mount_point_guess_type
+g_get_unix_mount_points
+g_get_unix_mounts
+g_get_unix_mount_at
+g_unix_mounts_changed_since
+g_unix_mount_points_changed_since
+g_unix_mount_monitor_new
+<SUBSECTION Standard>
+GUnixMountMonitorClass
+G_UNIX_MOUNT_MONITOR
+G_IS_UNIX_MOUNT_MONITOR
+G_TYPE_UNIX_MOUNT_MONITOR
+G_UNIX_MOUNT_MONITOR_CLASS
+G_IS_UNIX_MOUNT_MONITOR_CLASS
+<SUBSECTION Private>
+g_unix_mount_monitor_get_type
+</SECTION>
+
+<SECTION>
+<FILE>giomodule</FILE>
+<TITLE>GIOModule</TITLE>
+GIOModule
+g_io_module_new
+g_io_modules_ensure_loaded
+g_io_module_load
+g_io_module_unload
+<SUBSECTION Standard>
+GIOModuleClass
+G_IO_MODULE
+G_IO_IS_MODULE
+G_IO_TYPE_MODULE
+G_IO_MODULE_CLASS
+G_IO_IS_MODULE_CLASS
+G_IO_MODULE_GET_CLASS
+<SUBSECTION Private>
+g_io_module_get_type
+</SECTION>
+
+<SECTION>
+<FILE>gurifuncs</FILE>
+<TITLE>Uri functions</TITLE>
+G_URI_RESERVED_CHARS_GENERIC_DELIMITERS
+G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS
+G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT
+G_URI_RESERVED_CHARS_ALLOWED_IN_PATH
+G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO
+g_uri_unescape_string
+g_uri_unescape_segment
+g_uri_get_scheme
+g_uri_escape_string
+g_string_append_uri_escaped
+</SECTION>
diff --git a/docs/reference/gio/gio.types b/docs/reference/gio/gio.types
new file mode 100644 (file)
index 0000000..89c12a8
--- /dev/null
@@ -0,0 +1,51 @@
+g_icon_get_type
+g_seekable_get_type
+g_unix_mount_monitor_get_type
+g_data_output_stream_get_type
+g_socket_output_stream_get_type
+g_local_file_enumerator_get_type
+g_socket_input_stream_get_type
+g_dummy_file_get_type
+g_memory_input_stream_get_type
+g_volume_get_type
+g_file_monitor_get_type
+g_data_input_stream_get_type
+g_vfs_get_type
+g_mount_operation_get_type
+g_local_directory_monitor_get_type
+g_themed_icon_get_type
+g_local_file_input_stream_get_type
+g_file_enumerator_get_type
+g_async_result_get_type
+g_filename_completer_get_type
+g_local_file_get_type
+g_local_vfs_get_type
+g_filter_output_stream_get_type
+g_file_icon_get_type
+g_buffered_input_stream_get_type
+g_local_file_monitor_get_type
+g_union_volume_monitor_get_type
+g_output_stream_get_type
+g_unix_drive_get_type
+g_drive_get_type
+g_file_input_stream_get_type
+g_poll_file_monitor_get_type
+g_file_get_type
+g_filter_input_stream_get_type
+g_volume_monitor_get_type
+g_directory_monitor_get_type
+g_desktop_app_info_get_type
+g_io_module_get_type
+g_native_volume_monitor_get_type
+g_buffered_output_stream_get_type
+g_unix_volume_get_type
+g_input_stream_get_type
+g_app_info_get_type
+g_file_output_stream_get_type
+g_cancellable_get_type
+g_memory_output_stream_get_type
+g_simple_async_result_get_type
+g_loadable_icon_get_type
+g_local_file_output_stream_get_type
+g_file_info_get_type
+g_unix_volume_monitor_get_type
diff --git a/docs/reference/gio/version.xml.in b/docs/reference/gio/version.xml.in
new file mode 100644 (file)
index 0000000..d78bda9
--- /dev/null
@@ -0,0 +1 @@
+@VERSION@
diff --git a/gio-2.0-uninstalled.pc.in b/gio-2.0-uninstalled.pc.in
new file mode 100644 (file)
index 0000000..af027d3
--- /dev/null
@@ -0,0 +1,6 @@
+Name: GIO Uninstalled
+Description: glib I/O library, Not Installed
+Version: @VERSION@
+Requires: gobject-2.0-uninstalled,gmodule-no-export-2.0-uninstalled
+Libs: ${pc_top_builddir}/${pcfiledir}/gio/libgio-2.0.la
+Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@
diff --git a/gio-2.0.pc.in b/gio-2.0.pc.in
new file mode 100644 (file)
index 0000000..4c62e34
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: GIO
+Description: glib I/O library
+Version: @VERSION@
+Requires: gobject-2.0,gmodule-no-export-2.0
+Libs: -L${libdir} -lgio-2.0
+Cflags: 
diff --git a/gio-unix-2.0-uninstalled.pc.in b/gio-unix-2.0-uninstalled.pc.in
new file mode 100644 (file)
index 0000000..a45dc9e
--- /dev/null
@@ -0,0 +1,6 @@
+Name: GIO unix specific APIs
+Description: unix specific headers for glib I/O library, Not Installed
+Version: @VERSION@
+Requires: gobject-2.0-uninstalled,gmodule-no-export-2.0-uninstalled,gio-2.0-uninstalled
+Libs: ${pc_top_builddir}/${pcfiledir}/gio/libgio-2.0.la
+Cflags: -I${pc_top_builddir}/${pcfiledir}/@srcdir@
diff --git a/gio-unix-2.0.pc.in b/gio-unix-2.0.pc.in
new file mode 100644 (file)
index 0000000..2e81c07
--- /dev/null
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: GIO unix specific APIs
+Description: unix specific headers for glib I/O library
+Version: @VERSION@
+Requires: gobject-2.0,gmodule-no-export-2.0,gio-2.0
+Libs: -L${libdir} -lgio-2.0
+Cflags: -I${includedir}/gio-unix-2.0/
diff --git a/gio/Makefile.am b/gio/Makefile.am
new file mode 100644 (file)
index 0000000..31b3e5c
--- /dev/null
@@ -0,0 +1,225 @@
+NULL =
+
+SUBDIRS= 
+
+if OS_UNIX
+SUBDIRS += xdgmime
+endif
+
+AM_CPPFLAGS = \
+       -DG_LOG_DOMAIN=\"GLib-GIO\"                     \
+       -I$(top_builddir)                               \
+       -I$(top_srcdir)                                 \
+       -I$(top_srcdir)/glib                            \
+       -I$(top_srcdir)/gmodule                         \
+       $(GLIB_DEBUG_FLAGS)                             \
+       -DG_DISABLE_DEPRECATED                          \
+       -DGIO_MODULE_DIR=\"$(libdir)/gio/modules\"      
+
+lib_LTLIBRARIES = libgio-2.0.la
+
+marshal_sources = \
+        gio-marshal.h \
+        gio-marshal.c \
+       $(NULL)
+
+if CROSS_COMPILING
+  glib_genmarshal=$(GLIB_GENMARSHAL)
+else
+  glib_genmarshal=../gobject/glib-genmarshal
+endif
+
+gio-marshal.h: gio-marshal.list
+       $(glib_genmarshal) --prefix=_gio_marshal $(srcdir)/gio-marshal.list --header > $@
+
+gio-marshal.c: gio-marshal.h gio-marshal.list
+       (echo "#include \"gio-marshal.h\""; \
+       $(glib_genmarshal) --prefix=_gio_marshal $(srcdir)/gio-marshal.list --body) > $@
+
+local_sources = \
+       glocaldirectorymonitor.c        \
+       glocaldirectorymonitor.h        \
+       glocalfile.c                    \
+       glocalfile.h                    \
+       glocalfileenumerator.c          \
+       glocalfileenumerator.h          \
+       glocalfileinfo.c                \
+       glocalfileinfo.h                \
+       glocalfileinputstream.c         \
+       glocalfileinputstream.h         \
+       glocalfilemonitor.c             \
+       glocalfilemonitor.h             \
+       glocalfileoutputstream.c        \
+       glocalfileoutputstream.h        \
+       glocalvfs.c                     \
+       glocalvfs.h                     \
+       $(NULL)
+
+platform_libadd =
+appinfo_sources =
+
+if HAVE_INOTIFY
+SUBDIRS += inotify
+platform_libadd += inotify/libinotify.la
+endif
+
+SUBDIRS += .
+
+if HAVE_FAM
+SUBDIRS += fam
+endif
+
+if OS_UNIX
+appinfo_sources += gdesktopappinfo.c gdesktopappinfo.h
+platform_libadd += xdgmime/libxdgmime.la
+unix_sources = \
+       gunixdrive.c \
+       gunixdrive.h \
+       gunixmounts.c \
+       gunixmounts.h \
+       gunixvolume.c \
+       gunixvolume.h \
+       gunixvolumemonitor.c \
+       gunixvolumemonitor.h \
+       $(NULL)
+
+giounixincludedir=$(includedir)/gio-unix-2.0/gio
+giounixinclude_HEADERS = \
+       gunixmounts.h           \
+       $(NULL)
+endif
+
+if OS_WIN32
+appinfo_sources += gwin32appinfo.c gwin32appinfo.h
+platform_libadd += -lshlwapi
+endif
+
+libgio_2_0_la_SOURCES =                \
+       gappinfo.c              \
+       gasynchelper.c          \
+       gasynchelper.h          \
+       gasyncresult.c          \
+       gbufferedinputstream.c  \
+       gbufferedoutputstream.c \
+       gcancellable.c          \
+       gcontenttype.c          \
+       gcontenttypeprivate.h   \
+       gdatainputstream.c      \
+       gdataoutputstream.c     \
+       gdirectorymonitor.c     \
+       gdrive.c                \
+       gdriveprivate.h         \
+       gdummyfile.c            \
+       gfile.c                 \
+       gfileattribute.c        \
+       gfileenumerator.c       \
+       gfileicon.c             \
+       gfileinfo.c             \
+       gfileinputstream.c      \
+       gfilemonitor.c          \
+       gfilenamecompleter.c    \
+       gfileoutputstream.c     \
+       gfilterinputstream.c    \
+       gfilteroutputstream.c   \
+       gicon.c                 \
+       ginputstream.c          \
+       gioerror.c              \
+       giomodule.c             \
+       gioscheduler.c          \
+       gloadableicon.c         \
+       gmemoryinputstream.c    \
+       gmemoryoutputstream.c   \
+       gmountoperation.c       \
+       gnativevolumemonitor.c  \
+       gnativevolumemonitor.h  \
+       goutputstream.c         \
+       gpollfilemonitor.c      \
+       gpollfilemonitor.h      \
+       gseekable.c             \
+       gsimpleasyncresult.c    \
+       gsocketinputstream.c    \
+       gsocketoutputstream.c   \
+       gthemedicon.c           \
+       gunionvolumemonitor.c   \
+       gunionvolumemonitor.h   \
+       gurifuncs.c             \
+       gvfs.c                  \
+       gvolume.c               \
+       gvolumemonitor.c        \
+       gvolumeprivate.h        \
+       $(appinfo_sources)      \
+       $(unix_sources)         \
+       $(local_sources)        \
+       $(marshal_sources)      \
+       $(NULL)
+
+$(libgio_2_0_la_OBJECTS): $(marshal_sources)
+
+libgio_2_0_la_LIBADD = \
+       $(top_builddir)/glib/libglib-2.0.la             \
+       $(top_builddir)/gobject/libgobject-2.0.la       \
+       $(top_builddir)/gmodule/libgmodule-2.0.la       \
+       $(platform_libadd)                              \
+       $(SELINUX_LIBS)                                 \
+       $(GLIB_LIBS)                                    \
+       $(XATTR_LIBS)                                   \
+       $(NULL)
+
+if OS_WIN32
+no_undefined = -no-undefined
+endif
+
+libgio_2_0_la_LDFLAGS= -export-dynamic $(no_undefined) -export-symbols-regex '^g_.*'
+
+gioincludedir=$(includedir)/glib-2.0/gio/
+gioinclude_HEADERS = \
+       gappinfo.h              \
+       gasyncresult.h          \
+       gbufferedinputstream.h  \
+       gbufferedoutputstream.h \
+       gcancellable.h          \
+       gcontenttype.h          \
+       gdatainputstream.h      \
+       gdataoutputstream.h     \
+       gdirectorymonitor.h     \
+       gdrive.h                \
+       gdummyfile.h            \
+       gfile.h                 \
+       gfileattribute.h        \
+       gfileenumerator.h       \
+       gfileicon.h             \
+       gfileinfo.h             \
+       gfileinputstream.h      \
+       gfilemonitor.h          \
+       gfilenamecompleter.h    \
+       gfileoutputstream.h     \
+       gfilterinputstream.h    \
+       gfilteroutputstream.h   \
+       gicon.h                 \
+       ginputstream.h          \
+       gioerror.h              \
+       giomodule.h             \
+       gioscheduler.h          \
+       gloadableicon.h         \
+       gmemoryinputstream.h    \
+       gmemoryoutputstream.h   \
+       gmountoperation.h       \
+       goutputstream.h         \
+       gseekable.h             \       
+       gsimpleasyncresult.h    \
+       gsocketinputstream.h    \
+       gsocketoutputstream.h   \
+       gthemedicon.h           \
+       gurifuncs.h             \
+       gvfs.h                  \
+       gvolume.h               \
+       gvolumemonitor.h        \
+       $(NULL)
+
+EXTRA_DIST =                   \
+       gio-marshal.list        \
+       $(NULL)
+
+CLEANFILES =                   \
+       $(marshal_sources)      \
+       $(NULL)
diff --git a/gio/fam/Makefile.am b/gio/fam/Makefile.am
new file mode 100644 (file)
index 0000000..0e8b799
--- /dev/null
@@ -0,0 +1,33 @@
+NULL =
+
+module_flags = -export_dynamic -avoid-version -module -no-undefined -export-symbols-regex '^g_io_module_(load|unload)'
+
+giomodule_LTLIBRARIES = libgiofam.la
+giomoduledir = $(libdir)/gio/modules
+
+libgiofam_la_SOURCES =                 \
+       fam-helper.c                    \
+       fam-helper.h                    \
+       fam-module.c                    \
+       gfamdirectorymonitor.c          \
+       gfamdirectorymonitor.h          \
+       gfamfilemonitor.c               \
+       gfamfilemonitor.h               \
+       $(NULL)
+
+libgiofam_la_CFLAGS = \
+       -DG_LOG_DOMAIN=\"GLib-GIO\"     \
+       -I$(top_srcdir)                 \
+       -I$(top_srcdir)/glib            \
+       -I$(top_srcdir)/gmodule         \
+       -I$(top_srcdir)/gio             \
+       -DGIO_MODULE_DIR=\"$(libdir)/gio/modules\"  \
+       -DG_DISABLE_DEPRECATED
+
+libgiofam_la_LDFLAGS = $(module_flags)
+libgiofam_la_LIBADD = \
+               $(top_builddir)/gio/libgio-2.0.la \
+               $(GLIB_LIBS) \
+               $(FAM_LIBS) \
+               $(NULL)
+
diff --git a/gio/fam/fam-helper.c b/gio/fam/fam-helper.c
new file mode 100644 (file)
index 0000000..40c2c6a
--- /dev/null
@@ -0,0 +1,245 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#include "config.h"
+#include <fam.h>
+#include <gio/gfilemonitor.h>
+#include <gio/gdirectorymonitor.h>
+
+#include "fam-helper.h"
+
+static FAMConnection* fam_connection = NULL;
+static gint fam_watch_id = 0;
+G_LOCK_DEFINE_STATIC(fam_connection);
+
+struct _fam_sub
+{
+  gchar *pathname;
+  gboolean directory;
+  gpointer user_data;
+  gboolean cancelled;
+  FAMRequest request;
+};
+
+static GFileMonitorEvent 
+fam_event_to_file_monitor_event (enum FAMCodes code)
+{
+  switch (code)
+    {
+    case FAMChanged:
+      return G_FILE_MONITOR_EVENT_CHANGED;
+      break;
+    case FAMDeleted:
+      return G_FILE_MONITOR_EVENT_DELETED;
+      break;
+    case FAMCreated:
+      return G_FILE_MONITOR_EVENT_CREATED;
+      break;
+    default:
+      return -1;
+      break;
+    }
+}
+
+static gboolean
+fam_do_iter_unlocked (void)
+{
+  while (fam_connection != NULL && FAMPending (fam_connection)) {
+    FAMEvent ev;
+    fam_sub* sub = NULL;
+    gboolean cancelled;
+    
+    if (FAMNextEvent (fam_connection, &ev) != 1) {
+      FAMClose (fam_connection);
+      g_free (fam_connection);
+      g_source_remove (fam_watch_id);
+      fam_watch_id = 0;
+      fam_connection = NULL;
+      return FALSE;
+    }
+    
+    sub = (fam_sub*)ev.userdata;
+    cancelled = sub->cancelled;
+    if (ev.code == FAMAcknowledge && cancelled)
+      {
+       g_free (sub);
+       continue;
+      }
+    
+    if (cancelled)
+      continue;
+    
+    if (sub->directory)
+      {
+       GDirectoryMonitor* monitor = G_DIRECTORY_MONITOR (sub->user_data);
+       GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
+       gchar* path = NULL;
+       GFile *child, *parent;
+       
+       /* unsupported event */
+       if (eflags == -1)
+         continue;
+       
+       if (ev.filename[0] == '/')
+         path = g_strdup (ev.filename);
+       else
+         path = g_strdup_printf ("%s/%s", sub->pathname, ev.filename);
+
+       child = g_file_new_for_path (path);
+       parent = g_file_get_parent (child);
+       g_directory_monitor_emit_event (monitor, child, NULL, eflags);
+       g_free (path);
+       g_object_unref (child);
+       g_object_unref (parent);
+      } else {
+       GFile *child;
+       GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
+       GFileMonitorEvent eflags = fam_event_to_file_monitor_event (ev.code);
+       gchar* path = NULL;
+      
+       if (eflags == -1)
+         continue;
+       path = g_strdup (ev.filename);
+       child = g_file_new_for_path (path);
+       g_file_monitor_emit_event (monitor, child, NULL, eflags);
+       g_free (path);
+       g_object_unref (child);
+      }
+  }
+  
+  return TRUE;
+}
+
+static gboolean
+fam_callback (GIOChannel *source,
+              GIOCondition condition,
+              gpointer data)
+{
+  gboolean res;
+  G_LOCK (fam_connection);
+  
+  res = fam_do_iter_unlocked ();
+  
+  G_UNLOCK (fam_connection);
+  return res;
+}
+
+gboolean
+_fam_sub_startup (void)
+{
+  GIOChannel *ioc;
+  
+  G_LOCK (fam_connection);
+  
+  if (fam_connection == NULL) {
+    fam_connection = g_new0 (FAMConnection, 1);
+    if (FAMOpen2 (fam_connection, "gvfs user") != 0) {
+      g_warning ("FAMOpen failed, FAMErrno=%d\n", FAMErrno);
+      g_free (fam_connection);
+      fam_connection = NULL;
+      G_UNLOCK (fam_connection);
+      return FALSE;
+    }
+#ifdef HAVE_FAM_NO_EXISTS
+    /* This is a gamin extension that avoids sending all the Exists event for dir monitors */
+    FAMNoExists (fam_connection);
+#endif
+    ioc = g_io_channel_unix_new (FAMCONNECTION_GETFD(fam_connection));
+    fam_watch_id = g_io_add_watch (ioc,
+                                  G_IO_IN | G_IO_HUP | G_IO_ERR,
+                                  fam_callback, fam_connection);
+    g_io_channel_unref (ioc);
+  }
+  
+  G_UNLOCK (fam_connection);
+  
+  return TRUE;
+}
+
+fam_sub*
+_fam_sub_add (const gchar* pathname,
+             gboolean directory,
+             gpointer user_data)
+{
+  fam_sub *sub;
+
+  if (!_fam_sub_startup ())
+    return NULL;
+  
+  sub = g_new0 (fam_sub, 1);
+  sub->pathname = g_strdup (pathname);
+  sub->directory = directory;
+  sub->user_data = user_data;
+  
+  G_LOCK (fam_connection);
+  /* We need to queue up incoming messages to avoid blocking on write
+   *  if there are many monitors being canceled */
+  fam_do_iter_unlocked ();
+  
+  if (fam_connection == NULL) {
+    G_UNLOCK (fam_connection);
+    return NULL;
+  }
+  
+  if (directory)
+    FAMMonitorDirectory (fam_connection, pathname, &sub->request, sub);
+  else
+    FAMMonitorFile (fam_connection, pathname, &sub->request, sub);
+  
+  G_UNLOCK (fam_connection);
+  return sub;
+}
+
+gboolean
+_fam_sub_cancel (fam_sub* sub)
+{
+  if (sub->cancelled)
+    return TRUE;
+  
+  sub->cancelled = TRUE;
+  
+  G_LOCK (fam_connection);
+  /* We need to queue up incoming messages to avoid blocking on write
+   *  if there are many monitors being canceled */
+  fam_do_iter_unlocked ();
+  
+  if (fam_connection == NULL) {
+    G_UNLOCK (fam_connection);
+    return FALSE;
+  }
+  
+  FAMCancelMonitor (fam_connection, &sub->request);
+  
+  G_UNLOCK (fam_connection);
+  
+  return TRUE;
+}
+
+void
+_fam_sub_free (fam_sub* sub)
+{
+  g_free (sub->pathname);
+  g_free (sub);
+}
+
diff --git a/gio/fam/fam-helper.h b/gio/fam/fam-helper.h
new file mode 100644 (file)
index 0000000..e8acbfe
--- /dev/null
@@ -0,0 +1,37 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#ifndef __FAM_HELPER_H__
+#define __FAM_HELPER_H__
+
+typedef struct _fam_sub fam_sub;
+
+gboolean  _fam_sub_startup (void);
+fam_sub*  _fam_sub_add     (const gchar* pathname,
+                           gboolean     directory,
+                           gpointer     user_data);
+gboolean  _fam_sub_cancel  (fam_sub* sub);
+void      _fam_sub_free    (fam_sub* sub);
+
+#endif /* __FAM_HELPER_H__ */
diff --git a/gio/fam/fam-module.c b/gio/fam/fam-module.c
new file mode 100644 (file)
index 0000000..d7b9cfc
--- /dev/null
@@ -0,0 +1,41 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#include "giomodule.h"
+#include "gfamdirectorymonitor.h"
+#include "gfamfilemonitor.h"
+
+void
+g_io_module_load (GIOModule *module)
+{
+  g_fam_file_monitor_register (module);
+  g_fam_directory_monitor_register (module);
+}
+
+void
+g_io_module_unload (GIOModule   *module)
+{
+}
+
diff --git a/gio/fam/gfamdirectorymonitor.c b/gio/fam/gfamdirectorymonitor.c
new file mode 100644 (file)
index 0000000..8929830
--- /dev/null
@@ -0,0 +1,152 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#include <config.h>
+
+#include "gfamdirectorymonitor.h"
+#include "giomodule.h"
+
+#include "fam-helper.h"
+
+struct _GFamDirectoryMonitor
+{
+  GLocalDirectoryMonitor parent_instance;
+  fam_sub *sub;
+};
+
+static gboolean g_fam_directory_monitor_cancel (GDirectoryMonitor* monitor);
+
+G_DEFINE_DYNAMIC_TYPE (GFamDirectoryMonitor, g_fam_directory_monitor, G_TYPE_LOCAL_DIRECTORY_MONITOR)
+
+static void
+g_fam_directory_monitor_finalize (GObject *object)
+{
+  GFamDirectoryMonitor *fam_monitor = G_FAM_DIRECTORY_MONITOR (object);
+  fam_sub *sub = fam_monitor->sub;
+
+  if (sub) {
+    if (!_fam_sub_cancel (sub))
+      g_warning ("Unexpected error cancelling fam monitor");
+
+    _fam_sub_free (sub);
+    fam_monitor->sub = NULL;
+  }
+
+  if (G_OBJECT_CLASS (g_fam_directory_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_fam_directory_monitor_parent_class)->finalize) (object);
+}
+
+static GObject *
+g_fam_directory_monitor_constructor (GType type,
+                                    guint n_construct_properties,
+                                    GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GFamDirectoryMonitorClass *klass;
+  GObjectClass *parent_class;
+  GFamDirectoryMonitor *fam_monitor;
+  const gchar *dirname = NULL;
+  fam_sub *sub = NULL;
+  
+  klass = G_FAM_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_FAM_DIRECTORY_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  fam_monitor = G_FAM_DIRECTORY_MONITOR (obj);
+
+  dirname = G_LOCAL_DIRECTORY_MONITOR (obj)->dirname;
+  g_assert (dirname != NULL);
+
+  sub = _fam_sub_add (dirname, TRUE, fam_monitor);
+  /* FIXME: what to do about errors here? we can't return NULL or another
+   * kind of error and an assertion is probably too hard */
+  g_assert (sub != NULL);
+
+  fam_monitor->sub = sub;
+
+  return obj;
+}
+
+static void
+g_fam_directory_monitor_class_finalize (GFamDirectoryMonitorClass *klass)
+{
+}
+
+static gboolean
+g_fam_directory_monitor_is_supported (void)
+{
+  return _fam_sub_startup ();
+}
+
+static void
+g_fam_directory_monitor_class_init (GFamDirectoryMonitorClass* klass)
+{
+  GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+  GDirectoryMonitorClass *directory_monitor_class = G_DIRECTORY_MONITOR_CLASS (klass);
+  GLocalDirectoryMonitorClass *local_directory_monitor_class = G_LOCAL_DIRECTORY_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_fam_directory_monitor_finalize;
+  gobject_class->constructor = g_fam_directory_monitor_constructor;
+  directory_monitor_class->cancel = g_fam_directory_monitor_cancel;
+
+  local_directory_monitor_class->prio = 10;
+  local_directory_monitor_class->mount_notify = FALSE;
+  local_directory_monitor_class->is_supported = g_fam_directory_monitor_is_supported;
+}
+
+static void
+g_fam_directory_monitor_init (GFamDirectoryMonitor* monitor)
+{
+
+}
+
+static gboolean
+g_fam_directory_monitor_cancel (GDirectoryMonitor* monitor)
+{
+  GFamDirectoryMonitor *fam_monitor = G_FAM_DIRECTORY_MONITOR (monitor);
+  fam_sub *sub = fam_monitor->sub;
+
+  if (sub) {
+    if (!_fam_sub_cancel (sub))
+      g_warning ("Unexpected error cancelling fam monitor");
+
+    _fam_sub_free (sub);
+    fam_monitor->sub = NULL;
+  }
+
+  if (G_DIRECTORY_MONITOR_CLASS (g_fam_directory_monitor_parent_class)->cancel)
+    (*G_DIRECTORY_MONITOR_CLASS (g_fam_directory_monitor_parent_class)->cancel) (monitor);
+
+  return TRUE;
+}
+
+void
+g_fam_directory_monitor_register (GIOModule *module)
+{
+  g_fam_directory_monitor_register_type (G_TYPE_MODULE (module));
+}
+
diff --git a/gio/fam/gfamdirectorymonitor.h b/gio/fam/gfamdirectorymonitor.h
new file mode 100644 (file)
index 0000000..eefda88
--- /dev/null
@@ -0,0 +1,55 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#ifndef __G_FAM_DIRECTORY_MONITOR_H__
+#define __G_FAM_DIRECTORY_MONITOR_H__
+
+#include <glib-object.h>
+#include <string.h>
+#include <gio/gdirectorymonitor.h>
+#include "glocaldirectorymonitor.h"
+#include "giomodule.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FAM_DIRECTORY_MONITOR           (g_fam_directory_monitor_get_type ())
+#define G_FAM_DIRECTORY_MONITOR(o)                     (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FAM_DIRECTORY_MONITOR, GFamDirectoryMonitor))
+#define G_FAM_DIRECTORY_MONITOR_CLASS(k)               (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_FAM_DIRECTORY_MONITOR, GFamDirectoryMonitorClass))
+#define G_IS_FAM_DIRECTORY_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FAM_DIRECTORY_MONITOR))
+#define G_IS_FAM_DIRECTORY_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FAM_DIRECTORY_MONITOR))
+
+typedef struct _GFamDirectoryMonitor      GFamDirectoryMonitor;
+typedef struct _GFamDirectoryMonitorClass GFamDirectoryMonitorClass;
+
+struct _GFamDirectoryMonitorClass {
+  GLocalDirectoryMonitorClass parent_class;
+};
+
+GType g_fam_directory_monitor_get_type (void);
+void g_fam_directory_monitor_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __G_FAM_DIRECTORY_MONITOR_H__ */
diff --git a/gio/fam/gfamfilemonitor.c b/gio/fam/gfamfilemonitor.c
new file mode 100644 (file)
index 0000000..be36318
--- /dev/null
@@ -0,0 +1,150 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#include <config.h>
+
+#include "gfamfilemonitor.h"
+#include "giomodule.h"
+
+#include "fam-helper.h"
+
+struct _GFamFileMonitor
+{
+  GLocalFileMonitor parent_instance;
+  fam_sub *sub;
+};
+
+static gboolean g_fam_file_monitor_cancel (GFileMonitor* monitor);
+
+G_DEFINE_DYNAMIC_TYPE (GFamFileMonitor, g_fam_file_monitor, G_TYPE_LOCAL_FILE_MONITOR)
+
+static void
+g_fam_file_monitor_finalize (GObject *object)
+{
+  GFamFileMonitor *fam_monitor = G_FAM_FILE_MONITOR (object);
+  fam_sub *sub = fam_monitor->sub;
+
+  if (sub) {
+    if (!_fam_sub_cancel (sub))
+      g_warning ("Unexpected error cancelling fam monitor");
+    _fam_sub_free (sub);
+    fam_monitor->sub = NULL;
+  }
+
+  if (G_OBJECT_CLASS (g_fam_file_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_fam_file_monitor_parent_class)->finalize) (object);
+}
+
+static GObject *
+g_fam_file_monitor_constructor (GType type,
+                                guint n_construct_properties,
+                                GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GFamFileMonitorClass *klass;
+  GObjectClass *parent_class;
+  GFamFileMonitor *fam_monitor;
+  const gchar *filename = NULL;
+  fam_sub *sub = NULL;
+  
+  klass = G_FAM_FILE_MONITOR_CLASS (g_type_class_peek (G_TYPE_FAM_FILE_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  fam_monitor = G_FAM_FILE_MONITOR (obj);
+
+  filename = G_LOCAL_FILE_MONITOR (obj)->filename;
+
+  g_assert (filename != NULL);
+
+  sub = _fam_sub_add (filename, FALSE, fam_monitor);
+  /* FIXME: what to do about errors here? we can't return NULL or another
+   * kind of error and an assertion is probably too hard */
+  g_assert (sub != NULL);
+
+  fam_monitor->sub = sub;
+
+  return obj;
+}
+
+static void
+g_fam_file_monitor_class_finalize (GFamFileMonitorClass *klass)
+{
+}
+
+static gboolean
+g_fam_file_monitor_is_supported (void)
+{
+  return _fam_sub_startup ();
+}
+
+static void
+g_fam_file_monitor_class_init (GFamFileMonitorClass* klass)
+{
+  GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+  GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
+  GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_fam_file_monitor_finalize;
+  gobject_class->constructor = g_fam_file_monitor_constructor;
+  file_monitor_class->cancel = g_fam_file_monitor_cancel;
+
+  local_file_monitor_class->prio = 10;
+  local_file_monitor_class->is_supported = g_fam_file_monitor_is_supported;
+}
+
+static void
+g_fam_file_monitor_init (GFamFileMonitor* monitor)
+{
+
+}
+
+static gboolean
+g_fam_file_monitor_cancel (GFileMonitor* monitor)
+{
+  GFamFileMonitor *fam_monitor = G_FAM_FILE_MONITOR (monitor);
+  fam_sub *sub = fam_monitor->sub;
+
+  if (sub) {
+    if (!_fam_sub_cancel (sub))
+      g_warning ("Unexpected error cancelling fam monitor");
+    _fam_sub_free (sub);
+    fam_monitor->sub = NULL;
+  }
+
+  if (G_FILE_MONITOR_CLASS (g_fam_file_monitor_parent_class)->cancel)
+    (*G_FILE_MONITOR_CLASS (g_fam_file_monitor_parent_class)->cancel) (monitor);
+
+  return TRUE;
+}
+
+void
+g_fam_file_monitor_register (GIOModule *module)
+{
+  g_fam_file_monitor_register_type (G_TYPE_MODULE (module));
+}
+
diff --git a/gio/fam/gfamfilemonitor.h b/gio/fam/gfamfilemonitor.h
new file mode 100644 (file)
index 0000000..559384e
--- /dev/null
@@ -0,0 +1,55 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#ifndef __G_FAM_FILE_MONITOR_H__
+#define __G_FAM_FILE_MONITOR_H__
+
+#include <glib-object.h>
+#include <string.h>
+#include <gio/gfilemonitor.h>
+#include "glocalfilemonitor.h"
+#include "giomodule.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FAM_FILE_MONITOR                (g_fam_file_monitor_get_type ())
+#define G_FAM_FILE_MONITOR(o)                  (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FAM_FILE_MONITOR, GFamFileMonitor))
+#define G_FAM_FILE_MONITOR_CLASS(k)            (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_FAM_FILE_MONITOR, GFamFileMonitorClass))
+#define G_IS_FAM_FILE_MONITOR(o)               (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FAM_FILE_MONITOR))
+#define G_IS_FAM_FILE_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FAM_FILE_MONITOR))
+
+typedef struct _GFamFileMonitor      GFamFileMonitor;
+typedef struct _GFamFileMonitorClass GFamFileMonitorClass;
+
+struct _GFamFileMonitorClass {
+  GLocalFileMonitorClass parent_class;
+};
+
+GType g_fam_file_monitor_get_type (void);
+void g_fam_file_monitor_register (GIOModule *module);
+
+G_END_DECLS
+
+#endif /* __G_FAM_FILE_MONITOR_H__ */
diff --git a/gio/gappinfo.c b/gio/gappinfo.c
new file mode 100644 (file)
index 0000000..922d700
--- /dev/null
@@ -0,0 +1,535 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gappinfo.h"
+#include "glibintl.h"
+#include <gioerror.h>
+
+
+static void g_app_info_base_init (gpointer g_class);
+static void g_app_info_class_init (gpointer g_class,
+                                  gpointer class_data);
+
+
+GType
+g_app_info_get_type (void)
+{
+  static GType app_info_type = 0;
+
+  if (! app_info_type)
+    {
+      static const GTypeInfo app_info_info =
+      {
+        sizeof (GAppInfoIface), /* class_size */
+       g_app_info_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       g_app_info_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      app_info_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GAppInfo"),
+                               &app_info_info, 0);
+
+      g_type_interface_add_prerequisite (app_info_type, G_TYPE_OBJECT);
+    }
+
+  return app_info_type;
+}
+
+static void
+g_app_info_class_init (gpointer g_class,
+                      gpointer class_data)
+{
+}
+
+static void
+g_app_info_base_init (gpointer g_class)
+{
+}
+
+
+/**
+ * g_app_info_dup:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: a duplicate of @appinfo.
+ **/
+GAppInfo *
+g_app_info_dup (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->dup) (appinfo);
+}
+
+/**
+ * g_app_info_equal:
+ * @appinfo1: the first #GAppInfo.  
+ * @appinfo2: the second #GAppInfo.
+ *
+ * Returns: %TRUE if @appinfo1 is equal to @appinfo2. %FALSE otherwise.
+ *  
+ **/
+gboolean
+g_app_info_equal (GAppInfo    *appinfo1,
+                 GAppInfo    *appinfo2)
+{
+  GAppInfoIface *iface;
+
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo1), FALSE);
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo2), FALSE);
+
+  if (G_TYPE_FROM_INSTANCE (appinfo1) != G_TYPE_FROM_INSTANCE (appinfo2))
+    return FALSE;
+  
+  iface = G_APP_INFO_GET_IFACE (appinfo1);
+
+  return (* iface->equal) (appinfo1, appinfo2);
+}
+
+/**
+ * g_app_info_get_id:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: 
+ **/
+const char *
+g_app_info_get_id (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->get_id) (appinfo);
+}
+
+/**
+ * g_app_info_get_name:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: the name of the application for @appinfo.
+ **/
+const char *
+g_app_info_get_name (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->get_name) (appinfo);
+}
+
+/**
+ * g_app_info_get_description:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: a string containing a description of the 
+ * application @appinfo.  
+ * The returned string should be not freed when no longer needed.
+ **/
+const char *
+g_app_info_get_description (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->get_description) (appinfo);
+}
+
+/**
+ * g_app_info_get_executable:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: a string containing the @appinfo's application 
+ * binary's name.
+ **/
+const char *
+g_app_info_get_executable (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->get_executable) (appinfo);
+}
+
+
+/**
+ * g_app_info_set_as_default_for_type:
+ * @appinfo: a #GAppInfo.
+ * @content_type: the content type.
+ * @error: a #GError.
+ *
+ * Returns: %TRUE if the given @appinfo is the default 
+ * for the given @content_type. %FALSE if not, 
+ * or in case of an error.
+ **/
+gboolean
+g_app_info_set_as_default_for_type (GAppInfo    *appinfo,
+                                   const char  *content_type,
+                                   GError     **error)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+  g_return_val_if_fail (content_type != NULL, FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->set_as_default_for_type) (appinfo, content_type, error);
+}
+
+
+/**
+ * g_app_info_set_as_default_for_extension:
+ * @appinfo: a #GAppInfo.
+ * @extension: a string containing the file extension.
+ * @error: a #GError.
+ *
+ * Returns: %TRUE if the given @appinfo is the default 
+ * for the given @extension. %FALSE if not, 
+ * or in case of an error.
+ **/
+gboolean
+g_app_info_set_as_default_for_extension (GAppInfo  *appinfo,
+                                        const char *extension,
+                                        GError **error)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+  g_return_val_if_fail (extension != NULL, FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  if (iface->set_as_default_for_extension)
+    return (* iface->set_as_default_for_extension) (appinfo, extension, error);
+
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "g_app_info_set_as_default_for_extension not supported yet");
+  return FALSE;
+}
+
+
+/**
+ * g_app_info_add_supports_type:
+ * @appinfo: a #GAppInfo.
+ * @content_type: a string.
+ * @error: a #GError.
+ *
+ * Returns: %TRUE if @appinfo supports @content_type.
+ * %FALSE if not, or in case of an error.
+ **/
+gboolean
+g_app_info_add_supports_type (GAppInfo             *appinfo,
+                             const char           *content_type,
+                             GError              **error)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+  g_return_val_if_fail (content_type != NULL, FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  if (iface->add_supports_type)
+    return (* iface->add_supports_type) (appinfo, content_type, error);
+
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "g_app_info_add_supports_type not supported yet");
+  return FALSE;
+}
+
+
+/**
+ * g_app_info_can_remove_support_type:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: %TRUE if it is possible to remove supported 
+ * content types from a given @appinfo, %FALSE if not.
+ **/
+gboolean
+g_app_info_can_remove_supports_type (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  if (iface->can_remove_supports_type)
+    return (* iface->can_remove_supports_type) (appinfo);
+
+  return FALSE;
+}
+
+
+/**
+ * g_app_info_remove_supports_type:
+ * @appinfo: a #GAppInfo.
+ * @content_type: a string.
+ * @error: a #GError.
+ *
+ * Returns: %TRUE if @content_type support was removed
+ * from @appinfo. %FALSE if not.
+ **/
+gboolean
+g_app_info_remove_supports_type (GAppInfo *appinfo,
+                                const char *content_type,
+                                GError **error)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+  g_return_val_if_fail (content_type != NULL, FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  if (iface->remove_supports_type)
+    return (* iface->remove_supports_type) (appinfo, content_type, error);
+
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "g_app_info_remove_supports_type not supported yet");
+  return FALSE;
+}
+
+
+/**
+ * g_app_info_get_icon:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: the default #GIcon for @appinfo.
+ **/
+GIcon *
+g_app_info_get_icon (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), NULL);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->get_icon) (appinfo);
+}
+
+
+/**
+ * g_app_info_launch:
+ * @appinfo: a #GAppInfo.
+ * @files: a #GList of #GFile objects.
+ * @launch_context: a #GAppLaunchContext.
+ * @error: a #GError.
+ *
+ * Returns: %TRUE on successful launch.
+ **/
+gboolean
+g_app_info_launch (GAppInfo    *appinfo,
+                  GList       *files,
+                  GAppLaunchContext *launch_context,
+                  GError     **error)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->launch) (appinfo, files, launch_context, error);
+}
+
+
+/**
+ * g_app_info_supports_uris:
+ * @appinfo: a #GAppInfo.
+ *
+ * Returns: %TRUE if the @appinfo supports URIs.
+ **/
+gboolean
+g_app_info_supports_uris (GAppInfo *appinfo)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->supports_uris) (appinfo);
+}
+
+
+/**
+ * g_app_info_launch_uris:
+ * @appinfo: a #GAppInfo.
+ * @uris: a #GList containing URIs to launch. 
+ * @launch_context: a #GAppLaunchContext.
+ * @error: a #GError.
+ *
+ * Returns: %TRUE if the @appinfo was launched 
+ * with the given @uris.
+ **/
+gboolean
+g_app_info_launch_uris (GAppInfo    *appinfo,
+                       GList       *uris,
+                       GAppLaunchContext *launch_context,
+                       GError     **error)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->launch) (appinfo, uris, launch_context, error);
+}
+
+
+/**
+ * g_app_info_should_show:
+ * @appinfo: a #GAppInfo.
+ * @desktop_env: a string.
+ *
+ * Returns: %TRUE if the @GAppInfo should be shown,
+ * %FALSE otherwise.
+ **/
+gboolean
+g_app_info_should_show (GAppInfo    *appinfo,
+                       const char  *desktop_env)
+{
+  GAppInfoIface *iface;
+  
+  g_return_val_if_fail (G_IS_APP_INFO (appinfo), FALSE);
+
+  iface = G_APP_INFO_GET_IFACE (appinfo);
+
+  return (* iface->should_show) (appinfo, desktop_env);
+}
+
+G_DEFINE_TYPE (GAppLaunchContext, g_app_launch_context, G_TYPE_OBJECT);
+
+/**
+ * g_app_launch_context_new:
+ *
+ * Returns: A new #GAppLaunchContext.
+ **/
+GAppLaunchContext *
+g_app_launch_context_new (void)
+{
+  return g_object_new (G_TYPE_APP_LAUNCH_CONTEXT, NULL);
+}
+
+static void
+g_app_launch_context_class_init (GAppLaunchContextClass *klass)
+{
+}
+
+static void
+g_app_launch_context_init (GAppLaunchContext *launch_context)
+{
+}
+
+/**
+ * g_app_launch_context_get_display:
+ * @context: a #GAppLaunchContext.  
+ * @info: a #GAppInfo. 
+ * @files: a #GList of files.
+ *
+ **/
+char *
+g_app_launch_context_get_display (GAppLaunchContext *context,
+                                 GAppInfo          *info,
+                                 GList             *files)
+{
+  GAppLaunchContextClass *class;
+
+  g_return_val_if_fail (G_IS_APP_LAUNCH_CONTEXT (context), NULL);
+  g_return_val_if_fail (G_IS_APP_INFO (info), NULL);
+
+  class = G_APP_LAUNCH_CONTEXT_GET_CLASS (context);
+
+  if (class->get_display == NULL)
+    return NULL;
+
+  return class->get_display (context, info, files);
+}
+
+/**
+ * g_app_launch_context_get_startup_notify_id:
+ * @context: a #GAppLaunchContext.
+ * @info: a #GAppInfo.
+ * @files: a #GList of files.
+ *
+ **/
+char *
+g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
+                                           GAppInfo          *info,
+                                           GList             *files)
+{
+  GAppLaunchContextClass *class;
+
+  g_return_val_if_fail (G_IS_APP_LAUNCH_CONTEXT (context), NULL);
+  g_return_val_if_fail (G_IS_APP_INFO (info), NULL);
+
+  class = G_APP_LAUNCH_CONTEXT_GET_CLASS (context);
+
+  if (class->get_startup_notify_id == NULL)
+    return NULL;
+
+  return class->get_startup_notify_id (context, info, files);
+}
+
+
+/**
+ * g_app_launch_context_get_startup_notify_id:
+ * @context: a #GAppLaunchContext.
+ * @startup_notify_id: a string containing the startup ID of the application.
+ *
+ **/
+void
+g_app_launch_context_launch_failed (GAppLaunchContext *context,
+                                   const char *startup_notify_id)
+{
+  GAppLaunchContextClass *class;
+
+  g_return_if_fail (G_IS_APP_LAUNCH_CONTEXT (context));
+  g_return_if_fail (startup_notify_id != NULL);
+
+  class = G_APP_LAUNCH_CONTEXT_GET_CLASS (context);
+
+  if (class->launch_failed != NULL)
+    class->launch_failed (context, startup_notify_id);
+}
diff --git a/gio/gappinfo.h b/gio/gappinfo.h
new file mode 100644 (file)
index 0000000..0a88349
--- /dev/null
@@ -0,0 +1,206 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_APP_INFO_H__
+#define __G_APP_INFO_H__
+
+#include <glib-object.h>
+#include <gio/gicon.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_APP_INFO            (g_app_info_get_type ())
+#define G_APP_INFO(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_APP_INFO, GAppInfo))
+#define G_IS_APP_INFO(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_APP_INFO))
+#define G_APP_INFO_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_APP_INFO, GAppInfoIface))
+
+#define G_TYPE_APP_LAUNCH_CONTEXT         (g_app_launch_context_get_type ())
+#define G_APP_LAUNCH_CONTEXT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContext))
+#define G_APP_LAUNCH_CONTEXT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextClass))
+#define G_IS_APP_LAUNCH_CONTEXT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_APP_LAUNCH_CONTEXT))
+#define G_IS_APP_LAUNCH_CONTEXT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_APP_LAUNCH_CONTEXT))
+#define G_APP_LAUNCH_CONTEXT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_APP_LAUNCH_CONTEXT, GAppLaunchContextClass))
+
+typedef enum {
+  G_APP_INFO_CREATE_FLAGS_NONE = 0,
+  G_APP_INFO_CREATE_NEEDS_TERMINAL = (1<<0)
+} GAppInfoCreateFlags;
+
+typedef struct _GAppLaunchContext        GAppLaunchContext;
+typedef struct _GAppLaunchContextClass   GAppLaunchContextClass;
+typedef struct _GAppLaunchContextPrivate GAppLaunchContextPrivate;
+
+typedef struct _GAppInfo         GAppInfo; /* Dummy typedef */
+typedef struct _GAppInfoIface    GAppInfoIface;
+
+struct _GAppInfoIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  GAppInfo *          (*dup)                (GAppInfo                *appinfo);
+  gboolean            (*equal)              (GAppInfo                *appinfo1,
+                                            GAppInfo                *appinfo2);
+  const char *        (*get_id)             (GAppInfo                *appinfo);
+  const char *        (*get_name)           (GAppInfo                *appinfo);
+  const char *        (*get_description)    (GAppInfo                *appinfo);
+  const char *        (*get_executable)     (GAppInfo                *appinfo);
+  GIcon *             (*get_icon)           (GAppInfo                *appinfo);
+  gboolean            (*launch)             (GAppInfo                *appinfo,
+                                            GList                   *filenames,
+                                            GAppLaunchContext       *launch_context,
+                                            GError                 **error);
+  gboolean            (*supports_uris)      (GAppInfo                *appinfo);
+  gboolean            (*launch_uris)        (GAppInfo                *appinfo,
+                                            GList                   *uris,
+                                            GAppLaunchContext       *launch_context,
+                                            GError                 **error);
+  gboolean            (*should_show)        (GAppInfo                *appinfo,
+                                            const char              *desktop_env);
+  gboolean            (*supports_xdg_startup_notify) (GAppInfo       *appinfo);
+
+
+  /* For changing associations */
+  gboolean  (*set_as_default_for_type)      (GAppInfo           *appinfo,
+                                            const char         *content_type,
+                                            GError            **error);
+  gboolean  (*set_as_default_for_extension) (GAppInfo           *appinfo,
+                                            const char         *extension,
+                                            GError            **error);
+  gboolean  (*add_supports_type)            (GAppInfo           *appinfo,
+                                            const char         *content_type,
+                                            GError            **error);
+  gboolean  (*can_remove_supports_type)     (GAppInfo           *appinfo);
+  gboolean  (*remove_supports_type)         (GAppInfo           *appinfo,
+                                            const char         *content_type,
+                                            GError            **error);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+  void (*_g_reserved9) (void);
+  void (*_g_reserved10) (void);
+};
+
+GType g_app_info_get_type (void) G_GNUC_CONST;
+GType g_app_launch_context_get_type (void) G_GNUC_CONST;
+
+GAppInfo *  g_app_info_create_from_commandline      (const char           *commandline,
+                                                    const char           *application_name,
+                                                    GAppInfoCreateFlags   flags,
+                                                    GError              **error);
+GAppInfo *  g_app_info_dup                          (GAppInfo             *appinfo);
+gboolean    g_app_info_equal                        (GAppInfo             *appinfo1,
+                                                    GAppInfo             *appinfo2);
+const char *g_app_info_get_id                       (GAppInfo             *appinfo);
+const char *g_app_info_get_name                     (GAppInfo             *appinfo);
+const char *g_app_info_get_description              (GAppInfo             *appinfo);
+const char *g_app_info_get_executable               (GAppInfo             *appinfo);
+GIcon *     g_app_info_get_icon                     (GAppInfo             *appinfo);
+gboolean    g_app_info_launch                       (GAppInfo             *appinfo,
+                                                    GList                *files,
+                                                    GAppLaunchContext    *launch_context,
+                                                    GError              **error);
+gboolean    g_app_info_supports_uris                (GAppInfo             *appinfo);
+gboolean    g_app_info_launch_uris                  (GAppInfo             *appinfo,
+                                                    GList                *uris,
+                                                    GAppLaunchContext    *launch_context,
+                                                    GError              **error);
+gboolean    g_app_info_should_show                  (GAppInfo             *appinfo,
+                                                    const char           *desktop_env);
+
+gboolean    g_app_info_set_as_default_for_type      (GAppInfo             *appinfo,
+                                                    const char           *content_type,
+                                                    GError              **error);
+gboolean    g_app_info_set_as_default_for_extension (GAppInfo             *appinfo,
+                                                    const char           *extension,
+                                                    GError              **error);
+gboolean    g_app_info_add_supports_type            (GAppInfo             *appinfo,
+                                                    const char           *content_type,
+                                                    GError              **error);
+gboolean    g_app_info_can_remove_supports_type     (GAppInfo             *appinfo);
+gboolean    g_app_info_remove_supports_type         (GAppInfo             *appinfo,
+                                                    const char           *content_type,
+                                                    GError              **error);
+
+GList *   g_app_info_get_all                     (void);
+GList *   g_app_info_get_all_for_type            (const char  *content_type);
+GAppInfo *g_app_info_get_default_for_type        (const char  *content_type,
+                                                 gboolean     must_support_uris);
+GAppInfo *g_app_info_get_default_for_uri_scheme  (const char  *uri_scheme);
+
+/* TODO: missing operations:
+   add app as supporting a content type, but don't make it default
+   implement set_as_default_for_extension
+
+   can_remove, remove (as in, don't support a specific mimetype)
+*/
+
+
+struct _GAppLaunchContext
+{
+  GObject parent_instance;
+
+  GAppLaunchContextPrivate *priv;
+};
+
+struct _GAppLaunchContextClass
+{
+  GObjectClass parent_class;
+
+  char * (*get_display)           (GAppLaunchContext *context,
+                                  GAppInfo *info,
+                                  GList *files);
+  char * (*get_startup_notify_id) (GAppLaunchContext *context,
+                                  GAppInfo *info,
+                                  GList *files);
+  void   (*launch_failed)         (GAppLaunchContext *context,
+                                  const char *startup_notify_id);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GAppLaunchContext *g_app_launch_context_new                   (void);
+char *             g_app_launch_context_get_display           (GAppLaunchContext *context,
+                                                              GAppInfo          *info,
+                                                              GList             *files);
+char *             g_app_launch_context_get_startup_notify_id (GAppLaunchContext *context,
+                                                              GAppInfo          *info,
+                                                              GList             *files);
+void               g_app_launch_context_launch_failed         (GAppLaunchContext *context,
+                                                              const char *       startup_notify_id);
+
+G_END_DECLS
+
+#endif /* __G_APP_INFO_H__ */
diff --git a/gio/gasynchelper.c b/gio/gasynchelper.c
new file mode 100644 (file)
index 0000000..b6a9352
--- /dev/null
@@ -0,0 +1,163 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "gasynchelper.h"
+
+static void
+async_result_free (gpointer data)
+{
+  GAsyncResultData *res = data;
+
+  if (res->error)
+    g_error_free (res->error);
+
+  g_object_unref (res->async_object);
+  
+  g_free (res);
+}
+
+void
+_g_queue_async_result (GAsyncResultData *result,
+                      gpointer        async_object,
+                      GError         *error,
+                      gpointer        user_data,
+                      GSourceFunc     source_func)
+{
+  GSource *source;
+
+  g_return_if_fail (G_IS_OBJECT (async_object));
+  
+  result->async_object = g_object_ref (async_object);
+  result->user_data = user_data;
+  result->error = error;
+
+  source = g_idle_source_new ();
+  g_source_set_priority (source, G_PRIORITY_DEFAULT);
+  g_source_set_callback (source, source_func, result, async_result_free);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+}
+
+/*************************************************************************
+ *             fd source                                                 *
+ ************************************************************************/
+
+typedef struct 
+{
+  GSource source;
+  GPollFD pollfd;
+  GCancellable *cancellable;
+  gulong cancelled_tag;
+} FDSource;
+
+static gboolean 
+fd_source_prepare (GSource  *source,
+                  gint     *timeout)
+{
+  FDSource *fd_source = (FDSource *)source;
+  *timeout = -1;
+  
+  return g_cancellable_is_cancelled (fd_source->cancellable);
+}
+
+static gboolean 
+fd_source_check (GSource  *source)
+{
+  FDSource *fd_source = (FDSource *)source;
+
+  return
+    g_cancellable_is_cancelled  (fd_source->cancellable) ||
+    fd_source->pollfd.revents != 0;
+}
+
+static gboolean
+fd_source_dispatch (GSource     *source,
+                   GSourceFunc  callback,
+                   gpointer     user_data)
+
+{
+  GFDSourceFunc func = (GFDSourceFunc)callback;
+  FDSource *fd_source = (FDSource *)source;
+
+  g_assert (func != NULL);
+
+  return (*func) (user_data, fd_source->pollfd.revents, fd_source->pollfd.fd);
+}
+
+static void 
+fd_source_finalize (GSource *source)
+{
+  FDSource *fd_source = (FDSource *)source;
+
+  if (fd_source->cancelled_tag)
+    g_signal_handler_disconnect (fd_source->cancellable,
+                                fd_source->cancelled_tag);
+
+  if (fd_source->cancellable)
+    g_object_unref (fd_source->cancellable);
+}
+
+static GSourceFuncs fd_source_funcs = {
+  fd_source_prepare,
+  fd_source_check,
+  fd_source_dispatch,
+  fd_source_finalize
+};
+
+/* Might be called on another thread */
+static void
+fd_source_cancelled_cb (GCancellable *cancellable,
+                       gpointer data)
+{
+  /* Wake up the mainloop in case we're waiting on async calls with FDSource */
+  g_main_context_wakeup (NULL);
+}
+
+GSource *
+_g_fd_source_new (int fd,
+                 gushort events,
+                 GCancellable *cancellable)
+{
+  GSource *source;
+  FDSource *fd_source;
+
+  source = g_source_new (&fd_source_funcs, sizeof (FDSource));
+  fd_source = (FDSource *)source;
+
+  if (cancellable)
+    fd_source->cancellable = g_object_ref (cancellable);
+  
+  fd_source->pollfd.fd = fd;
+  fd_source->pollfd.events = events;
+  g_source_add_poll (source, &fd_source->pollfd);
+
+  if (cancellable)
+    fd_source->cancelled_tag =
+      g_signal_connect_data (cancellable, "cancelled",
+                            (GCallback)fd_source_cancelled_cb,
+                            NULL, NULL,
+                            0);
+  
+  return source;
+}
diff --git a/gio/gasynchelper.h b/gio/gasynchelper.h
new file mode 100644 (file)
index 0000000..0f57a40
--- /dev/null
@@ -0,0 +1,53 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_ASYNC_HELPER_H__
+#define __G_ASYNC_HELPER_H__
+
+#include <glib-object.h>
+#include "gcancellable.h"
+
+G_BEGIN_DECLS
+
+typedef struct {
+  gpointer       async_object;
+  GError *       error;
+  gpointer       user_data;
+} GAsyncResultData;
+
+typedef gboolean (*GFDSourceFunc) (gpointer user_data,
+                                  GIOCondition condition,
+                                  int fd);
+
+void     _g_queue_async_result (GAsyncResultData *result,
+                               gpointer         async_object,
+                               GError          *error,
+                               gpointer         user_data,
+                               GSourceFunc      source_func);
+
+GSource *_g_fd_source_new      (int              fd,
+                               gushort          events,
+                               GCancellable    *cancellable);
+
+G_END_DECLS
+
+#endif /* __G_ASYNC_HELPER_H__ */
diff --git a/gio/gasyncresult.c b/gio/gasyncresult.c
new file mode 100644 (file)
index 0000000..8409319
--- /dev/null
@@ -0,0 +1,107 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gasyncresult.h"
+#include "glibintl.h"
+
+static void g_async_result_base_init (gpointer g_class);
+static void g_async_result_class_init (gpointer g_class,
+                                      gpointer class_data);
+
+GType
+g_async_result_get_type (void)
+{
+  static GType async_result_type = 0;
+
+  if (! async_result_type)
+    {
+      static const GTypeInfo async_result_info =
+      {
+        sizeof (GAsyncResultIface), /* class_size */
+       g_async_result_base_init,   /* base_init */
+       NULL,                       /* base_finalize */
+       g_async_result_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      async_result_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GAsyncResult"),
+                               &async_result_info, 0);
+
+      g_type_interface_add_prerequisite (async_result_type, G_TYPE_OBJECT);
+    }
+
+  return async_result_type;
+}
+
+static void
+g_async_result_class_init (gpointer g_class,
+                          gpointer class_data)
+{
+}
+
+static void
+g_async_result_base_init (gpointer g_class)
+{
+}
+
+/**
+ * g_async_result_get_user_data:
+ * @res: a #GAsyncResult.
+ * 
+ * Returns: the user data for the given @res, or
+ * %NULL on failure. 
+ **/
+gpointer
+g_async_result_get_user_data (GAsyncResult *res)
+{
+  GAsyncResultIface *iface;
+
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  iface = G_ASYNC_RESULT_GET_IFACE (res);
+
+  return (* iface->get_user_data) (res);
+}
+
+/**
+ * g_async_result_get_source_object:
+ * @res: a #GAsyncResult.
+ * 
+ * Returns: the source object for the @res.
+ **/
+GObject *
+g_async_result_get_source_object (GAsyncResult *res)
+{
+  GAsyncResultIface *iface;
+
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  iface = G_ASYNC_RESULT_GET_IFACE (res);
+
+  return (* iface->get_source_object) (res);
+}
diff --git a/gio/gasyncresult.h b/gio/gasyncresult.h
new file mode 100644 (file)
index 0000000..26f867e
--- /dev/null
@@ -0,0 +1,59 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_ASYNC_RESULT_H__
+#define __G_ASYNC_RESULT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_ASYNC_RESULT            (g_async_result_get_type ())
+#define G_ASYNC_RESULT(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ASYNC_RESULT, GAsyncResult))
+#define G_IS_ASYNC_RESULT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ASYNC_RESULT))
+#define G_ASYNC_RESULT_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ASYNC_RESULT, GAsyncResultIface))
+
+typedef struct _GAsyncResult         GAsyncResult; /* Dummy typedef */
+typedef struct _GAsyncResultIface    GAsyncResultIface;
+
+typedef void (*GAsyncReadyCallback) (GObject *source_object,
+                                    GAsyncResult *res,
+                                    gpointer user_data);
+
+struct _GAsyncResultIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  gpointer   (*get_user_data)      (GAsyncResult                *async_result);
+  GObject *  (*get_source_object)  (GAsyncResult                *async_result);
+};
+
+GType g_async_result_get_type (void) G_GNUC_CONST;
+
+gpointer g_async_result_get_user_data     (GAsyncResult *res);
+GObject *g_async_result_get_source_object (GAsyncResult *res);
+
+G_END_DECLS
+
+#endif /* __G_ASYNC_RESULT_H__ */
diff --git a/gio/gbufferedinputstream.c b/gio/gbufferedinputstream.c
new file mode 100644 (file)
index 0000000..18469a0
--- /dev/null
@@ -0,0 +1,1230 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#include <config.h>
+
+#include "gbufferedinputstream.h"
+#include "ginputstream.h"
+#include "gsimpleasyncresult.h"
+#include <string.h>
+
+#include "glibintl.h"
+
+#define DEFAULT_BUFFER_SIZE 4096
+
+struct _GBufferedInputStreamPrivate {
+  guint8 *buffer;
+  gsize   len;
+  gsize   pos;
+  gsize   end;
+  GAsyncReadyCallback outstanding_callback;
+};
+
+enum {
+  PROP_0,
+  PROP_BUFSIZE
+};
+
+static void g_buffered_input_stream_set_property  (GObject      *object,
+                                                   guint         prop_id,
+                                                   const GValue *value,
+                                                   GParamSpec   *pspec);
+
+static void g_buffered_input_stream_get_property  (GObject      *object,
+                                                   guint         prop_id,
+                                                   GValue       *value,
+                                                   GParamSpec   *pspec);
+static void g_buffered_input_stream_finalize      (GObject *object);
+
+
+static gssize g_buffered_input_stream_skip             (GInputStream          *stream,
+                                                       gsize                  count,
+                                                       GCancellable          *cancellable,
+                                                       GError               **error);
+static void   g_buffered_input_stream_skip_async       (GInputStream          *stream,
+                                                       gsize                  count,
+                                                       int                    io_priority,
+                                                       GCancellable          *cancellable,
+                                                       GAsyncReadyCallback    callback,
+                                                       gpointer               user_data);
+static gssize g_buffered_input_stream_skip_finish      (GInputStream          *stream,
+                                                       GAsyncResult          *result,
+                                                       GError               **error);
+static gssize g_buffered_input_stream_read             (GInputStream          *stream,
+                                                       void                  *buffer,
+                                                       gsize                  count,
+                                                       GCancellable          *cancellable,
+                                                       GError               **error);
+static void   g_buffered_input_stream_read_async       (GInputStream          *stream,
+                                                       void                  *buffer,
+                                                       gsize                  count,
+                                                       int                    io_priority,
+                                                       GCancellable          *cancellable,
+                                                       GAsyncReadyCallback    callback,
+                                                       gpointer               user_data);
+static gssize g_buffered_input_stream_read_finish      (GInputStream          *stream,
+                                                       GAsyncResult          *result,
+                                                       GError               **error);
+static gssize g_buffered_input_stream_real_fill        (GBufferedInputStream  *stream,
+                                                       gssize                 count,
+                                                       GCancellable          *cancellable,
+                                                       GError               **error);
+static void   g_buffered_input_stream_real_fill_async  (GBufferedInputStream  *stream,
+                                                       gssize                 count,
+                                                       int                    io_priority,
+                                                       GCancellable          *cancellable,
+                                                       GAsyncReadyCallback    callback,
+                                                       gpointer               user_data);
+static gssize g_buffered_input_stream_real_fill_finish (GBufferedInputStream  *stream,
+                                                       GAsyncResult          *result,
+                                                       GError               **error);
+
+static void compact_buffer (GBufferedInputStream *stream);
+
+G_DEFINE_TYPE (GBufferedInputStream,
+               g_buffered_input_stream,
+               G_TYPE_FILTER_INPUT_STREAM)
+
+
+static void
+g_buffered_input_stream_class_init (GBufferedInputStreamClass *klass)
+{
+  GObjectClass *object_class;
+  GInputStreamClass *istream_class;
+  GBufferedInputStreamClass *bstream_class;
+
+  g_type_class_add_private (klass, sizeof (GBufferedInputStreamPrivate));
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = g_buffered_input_stream_get_property;
+  object_class->set_property = g_buffered_input_stream_set_property;
+  object_class->finalize     = g_buffered_input_stream_finalize;
+
+  istream_class = G_INPUT_STREAM_CLASS (klass);
+  istream_class->skip = g_buffered_input_stream_skip;
+  istream_class->skip_async  = g_buffered_input_stream_skip_async;
+  istream_class->skip_finish = g_buffered_input_stream_skip_finish;
+  istream_class->read = g_buffered_input_stream_read;
+  istream_class->read_async  = g_buffered_input_stream_read_async;
+  istream_class->read_finish = g_buffered_input_stream_read_finish;
+
+  bstream_class = G_BUFFERED_INPUT_STREAM_CLASS (klass);
+  bstream_class->fill = g_buffered_input_stream_real_fill;
+  bstream_class->fill_async = g_buffered_input_stream_real_fill_async;
+  bstream_class->fill_finish = g_buffered_input_stream_real_fill_finish;
+  
+  g_object_class_install_property (object_class,
+                                   PROP_BUFSIZE,
+                                   g_param_spec_uint ("buffer-size",
+                                                      P_("Buffer Size"),
+                                                      P_("The size of the backend buffer"),
+                                                      1,
+                                                      G_MAXUINT,
+                                                      DEFAULT_BUFFER_SIZE,
+                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT |
+                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+
+}
+
+/**
+ * g_buffered_input_stream_get_buffer_size:
+ * @stream: #GBufferedInputStream.
+ * 
+ * Returns: the current buffer size, or -1 on error.
+ **/
+gsize
+g_buffered_input_stream_get_buffer_size (GBufferedInputStream  *stream)
+{
+  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
+
+  return stream->priv->len;
+}
+
+/**
+ * g_buffered_input_stream_set_buffer_size:
+ * @stream: #GBufferedInputStream.
+ * @size: a #gsize.
+ *
+ * Sets the size of the internal buffer of @stream to @size, or to the 
+ * size of the contents of the buffer. The buffer can never be resized 
+ * smaller than its current contents.
+ **/
+void
+g_buffered_input_stream_set_buffer_size (GBufferedInputStream  *stream,
+                                        gsize                  size)
+{
+  GBufferedInputStreamPrivate *priv;
+  gsize in_buffer;
+  guint8 *buffer;
+  
+  g_return_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream));
+
+  priv = stream->priv;
+
+  if (priv->buffer)
+    {
+      in_buffer = priv->end - priv->pos;
+
+      /* Never resize smaller than current buffer contents */
+      size = MAX (size, in_buffer);
+
+      buffer = g_malloc (size);
+      memcpy (buffer, priv->buffer + priv->pos, in_buffer);
+      priv->len = size;
+      priv->pos = 0;
+      priv->end = in_buffer;
+      g_free (priv->buffer);
+      priv->buffer = buffer;
+    }
+  else
+    {
+      priv->len = size;
+      priv->pos = 0;
+      priv->end = 0;
+      priv->buffer = g_malloc (size);
+    }
+}
+
+static void
+g_buffered_input_stream_set_property (GObject         *object,
+                                      guint            prop_id,
+                                      const GValue    *value,
+                                      GParamSpec      *pspec)
+{
+  GBufferedInputStreamPrivate *priv;
+  GBufferedInputStream        *bstream;
+
+  bstream = G_BUFFERED_INPUT_STREAM (object);
+  priv = bstream->priv;
+
+  switch (prop_id) 
+    {
+    case PROP_BUFSIZE:
+      g_buffered_input_stream_set_buffer_size (bstream, g_value_get_uint (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_buffered_input_stream_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  GBufferedInputStreamPrivate *priv;
+  GBufferedInputStream        *bstream;
+
+  bstream = G_BUFFERED_INPUT_STREAM (object);
+  priv = bstream->priv;
+
+  switch (prop_id)
+    { 
+    
+    case PROP_BUFSIZE:
+      g_value_set_uint (value, priv->len);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_buffered_input_stream_finalize (GObject *object)
+{
+  GBufferedInputStreamPrivate *priv;
+  GBufferedInputStream        *stream;
+
+  stream = G_BUFFERED_INPUT_STREAM (object);
+  priv = stream->priv;
+
+  g_free (priv->buffer);
+
+  if (G_OBJECT_CLASS (g_buffered_input_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_buffered_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_buffered_input_stream_init (GBufferedInputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                              G_TYPE_BUFFERED_INPUT_STREAM,
+                                              GBufferedInputStreamPrivate);
+}
+
+
+/**
+ * g_buffered_input_stream_new:
+ * @base_stream: a #GInputStream.
+ * 
+ * Creates a new #GInputStream from the given @base_stream, with 
+ * a buffer set to the default size (4 kilobytes).
+ *
+ * Returns: a #GInputStream for the given @base_stream.
+ **/
+GInputStream *
+g_buffered_input_stream_new (GInputStream *base_stream)
+{
+  GInputStream *stream;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
+
+  stream = g_object_new (G_TYPE_BUFFERED_INPUT_STREAM,
+                         "base-stream", base_stream,
+                         NULL);
+
+  return stream;
+}
+
+/**
+ * g_buffered_input_stream_new_sized:
+ * @base_stream: a #GOutputStream.
+ * @size: a #gsize.
+ * 
+ * Creates a new #GBufferedInputStream from the given @base_stream, with 
+ * a buffer set to @size.
+ *
+ * Returns: a #GInputStream.
+ **/
+GInputStream *
+g_buffered_input_stream_new_sized (GInputStream *base_stream,
+                                   gsize         size)
+{
+  GInputStream *stream;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
+
+  stream = g_object_new (G_TYPE_BUFFERED_INPUT_STREAM,
+                         "base-stream", base_stream,
+                         "buffer-size", (guint)size,
+                         NULL);
+
+  return stream;
+}
+
+/**
+ * g_buffered_input_stream_fill:
+ * @stream: #GBufferedInputStream.
+ * @count: the number of bytes that will be read from the stream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore.
+ *
+ * Tries to read @count bytes from the stream into the buffer. 
+ * Will block during this read.
+ * 
+ * If @count is zero, returns zero and does nothing. A value of @count
+ * larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
+ *
+ * On success, the number of bytes read into the buffer is returned.
+ * It is not an error if this is not the same as the requested size, as it
+ * can happen e.g. near the end of a file. Zero is returned on end of file
+ * (or if @count is zero),  but never otherwise.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ * On error -1 is returned and @error is set accordingly.
+ * 
+ * For the asynchronous, non-blocking, version of this function, see 
+ * g_buffered_input_stream_fill_async().
+ *
+ * Returns: the number of bytes read into @stream's buffer, up to @count, 
+ * or -1 on error.
+ **/
+gssize
+g_buffered_input_stream_fill (GBufferedInputStream  *stream,
+                             gssize                 count,
+                             GCancellable          *cancellable,
+                             GError               **error)
+{
+  GBufferedInputStreamClass *class;
+  GInputStream *input_stream;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
+  
+  input_stream = G_INPUT_STREAM (stream);
+  
+  if (g_input_stream_is_closed (input_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return -1;
+    }
+  
+  if (g_input_stream_has_pending (input_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return -1;
+    }
+      
+  g_input_stream_set_pending (input_stream, TRUE);
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
+  res = class->fill (stream, count, cancellable, error);
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  g_input_stream_set_pending (input_stream, FALSE);
+  
+  return res;
+}
+
+static void
+async_fill_callback_wrapper (GObject *source_object,
+                            GAsyncResult *res,
+                            gpointer      user_data)
+{
+  GBufferedInputStream *stream = G_BUFFERED_INPUT_STREAM (source_object);
+
+  g_input_stream_set_pending (G_INPUT_STREAM (stream), FALSE);
+  (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+/**
+ * g_buffered_input_stream_fill_async:
+ * @stream: #GBufferedInputStream.
+ * @count: a #gssize.
+ * @io_priority: the io priority of the request. the io priority of the request.
+ * @cancellable: optional #GCancellable object
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: a #gpointer.
+ *
+ * Reads data into @stream's buffer asynchronously, up to @count size.
+ * @io_priority can be used to prioritize reads. For the synchronous
+ * version of this function, see g_buffered_input_stream_fill().
+ * 
+ **/
+
+void
+g_buffered_input_stream_fill_async (GBufferedInputStream  *stream,
+                                   gssize                 count,
+                                   int                    io_priority,
+                                   GCancellable          *cancellable,
+                                   GAsyncReadyCallback    callback,
+                                   gpointer               user_data)
+{
+  GBufferedInputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream));
+
+  if (count == 0)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_buffered_input_stream_fill_async);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+  
+  if (((gssize) count) < 0)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                          _("Too large count value passed to g_input_stream_read_async"));
+      return;
+    }
+  
+  if (g_input_stream_is_closed (G_INPUT_STREAM (stream)))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+    
+  if (g_input_stream_has_pending (G_INPUT_STREAM (stream)))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
+  
+  g_input_stream_set_pending (G_INPUT_STREAM (stream), TRUE);
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->fill_async (stream, count, io_priority, cancellable,
+                    async_fill_callback_wrapper, user_data);
+}
+
+/**
+ * g_buffered_input_stream_fill_finished:
+ * @stream: a #GBufferedInputStream.
+ * @result: a #GAsyncResult.
+ * @error: a #GError.
+ *
+ * Finishes an asynchronous read.
+ * 
+ * Returns: a #gssize of the read stream, or -1 on an error. 
+ **/
+gssize
+g_buffered_input_stream_fill_finish (GBufferedInputStream  *stream,
+                                    GAsyncResult          *result,
+                                    GError               **error)
+{
+  GSimpleAsyncResult *simple;
+  GBufferedInputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return -1;
+
+      /* Special case read of 0 bytes */
+      if (g_simple_async_result_get_source_tag (simple) == g_buffered_input_stream_fill_async)
+       return 0;
+    }
+
+  class = G_BUFFERED_INPUT_STREAM_GET_CLASS (stream);
+  return class->fill_finish (stream, result, error);
+}
+
+/**
+ * g_buffered_input_stream_get_available:
+ * @stream: #GBufferedInputStream.
+ * 
+ * Returns: size of the available stream. 
+ **/
+gsize
+g_buffered_input_stream_get_available (GBufferedInputStream  *stream)
+{
+  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
+
+  return stream->priv->end - stream->priv->pos;
+}
+
+/**
+ * g_buffered_input_stream_peek:
+ * @stream: a #GBufferedInputStream.
+ * @buffer: a pointer to an allocated chunk of memory.
+ * @offset: a #gsize.
+ * @count: a #gsize.
+ * 
+ * Returns: 
+ **/
+gsize
+g_buffered_input_stream_peek (GBufferedInputStream  *stream,
+                             void                  *buffer,
+                             gsize                  offset,
+                             gsize                  count)
+{
+  gsize available;
+  gsize end;
+  
+  g_return_val_if_fail (G_IS_BUFFERED_INPUT_STREAM (stream), -1);
+  g_return_val_if_fail (buffer != NULL, -1);
+
+  available = g_buffered_input_stream_get_available (stream);
+
+  if (offset > available)
+    return 0;
+  
+  end = MIN (offset + count, available);
+  count = end - offset;
+  
+  memcpy (buffer, stream->priv->buffer + stream->priv->pos + offset, count);
+  return count;
+}
+
+static void
+compact_buffer (GBufferedInputStream *stream)
+{
+  GBufferedInputStreamPrivate *priv;
+  gsize current_size;
+
+  priv = stream->priv;
+
+  current_size = priv->end - priv->pos;
+  
+  g_memmove (priv->buffer,
+            priv->buffer + priv->pos,
+            current_size);
+  
+  priv->pos = 0;
+  priv->end = current_size;
+}
+
+static gssize
+g_buffered_input_stream_real_fill (GBufferedInputStream *stream,
+                                  gssize                 count,
+                                  GCancellable         *cancellable,
+                                  GError              **error)
+{
+  GBufferedInputStreamPrivate *priv;
+  GInputStream *base_stream;
+  gssize nread;
+  gsize in_buffer;
+
+  priv = stream->priv;
+
+  if (count == -1)
+    count = priv->len;
+  
+  in_buffer = priv->end - priv->pos;
+
+  /* Never fill more than can fit in the buffer */
+  count = MIN (count, priv->len - in_buffer);
+
+  /* If requested length does not fit at end, compact */
+  if (priv->len - priv->end < count)
+    compact_buffer (stream);
+
+  base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+  nread = g_input_stream_read (base_stream,
+                               priv->buffer + priv->end,
+                               count,
+                               cancellable,
+                               error);
+
+  if (nread > 0)
+    priv->end += nread;
+  
+  return nread;
+}
+
+static gssize
+g_buffered_input_stream_skip (GInputStream          *stream,
+                             gsize                  count,
+                             GCancellable          *cancellable,
+                             GError               **error)
+{
+  GBufferedInputStream        *bstream;
+  GBufferedInputStreamPrivate *priv;
+  GInputStream *base_stream;
+  gsize available, bytes_skipped;
+  gssize nread;
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+  priv = bstream->priv;
+
+  available = priv->end - priv->pos;
+
+  if (count <= available)
+    {
+      priv->pos += count;
+      return count;
+    }
+
+  /* Full request not available, skip all currently availbile and request refill for more */
+  
+  priv->pos = 0;
+  priv->end = 0;
+  bytes_skipped = available;
+  count -= available;
+
+  if (bytes_skipped > 0)
+    error = NULL; /* Ignore further errors if we already read some data */
+  
+  if (count > priv->len)
+    {
+      /* Large request, shortcut buffer */
+      
+      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+
+      nread = g_input_stream_skip (base_stream,
+                                  count,
+                                  cancellable,
+                                  error);
+      
+      if (nread < 0 && bytes_skipped == 0)
+       return -1;
+      
+      if (nread > 0)
+       bytes_skipped += nread;
+      
+      return bytes_skipped;
+    }
+  
+  g_input_stream_set_pending (stream, FALSE); /* to avoid already pending error */
+  nread = g_buffered_input_stream_fill (bstream, priv->len, cancellable, error);
+  g_input_stream_set_pending (stream, TRUE); /* enable again */
+  
+  if (nread < 0)
+    {
+      if (bytes_skipped == 0)
+       return -1;
+      else
+       return bytes_skipped;
+    }
+  
+  available = priv->end - priv->pos;
+  count = MIN (count, available);
+  
+  bytes_skipped += count;
+  priv->pos += count;
+  
+  return bytes_skipped;
+}
+
+static gssize
+g_buffered_input_stream_read (GInputStream *stream,
+                              void         *buffer,
+                              gsize         count,
+                              GCancellable *cancellable,
+                              GError      **error)
+{
+  GBufferedInputStream        *bstream;
+  GBufferedInputStreamPrivate *priv;
+  GInputStream *base_stream;
+  gsize available, bytes_read;
+  gssize nread;
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+  priv = bstream->priv;
+
+  available = priv->end - priv->pos;
+
+  if (count <= available)
+    {
+      memcpy (buffer, priv->buffer + priv->pos, count);
+      priv->pos += count;
+      return count;
+    }
+  
+  /* Full request not available, read all currently availbile and request refill for more */
+  
+  memcpy (buffer, priv->buffer + priv->pos, available);
+  priv->pos = 0;
+  priv->end = 0;
+  bytes_read = available;
+  count -= available;
+
+  if (bytes_read > 0)
+    error = NULL; /* Ignore further errors if we already read some data */
+  
+  if (count > priv->len)
+    {
+      /* Large request, shortcut buffer */
+      
+      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+
+      nread = g_input_stream_read (base_stream,
+                                  (char *)buffer + bytes_read,
+                                  count,
+                                  cancellable,
+                                  error);
+      
+      if (nread < 0 && bytes_read == 0)
+       return -1;
+      
+      if (nread > 0)
+       bytes_read += nread;
+      
+      return bytes_read;
+    }
+  
+  g_input_stream_set_pending (stream, FALSE); /* to avoid already pending error */
+  nread = g_buffered_input_stream_fill (bstream, priv->len, cancellable, error);
+  g_input_stream_set_pending (stream, TRUE); /* enable again */
+  if (nread < 0)
+    {
+      if (bytes_read == 0)
+       return -1;
+      else
+       return bytes_read;
+    }
+  
+  available = priv->end - priv->pos;
+  count = MIN (count, available);
+  
+  memcpy ((char *)buffer + bytes_read, (char *)priv->buffer + priv->pos, count);
+  bytes_read += count;
+  priv->pos += count;
+  
+  return bytes_read;
+}
+
+/* ************************** */
+/* Async stuff implementation */
+/* ************************** */
+
+static void
+fill_async_callback (GObject *source_object,
+                    GAsyncResult *result,
+                    gpointer user_data)
+{
+  GError *error;
+  gssize res;
+  GSimpleAsyncResult *simple;
+
+  simple = user_data;
+  
+  error = NULL;
+  res = g_input_stream_read_finish (G_INPUT_STREAM (source_object),
+                                   result, &error);
+
+  g_simple_async_result_set_op_res_gssize (simple, res);
+  if (res == -1)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+  
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+g_buffered_input_stream_real_fill_async  (GBufferedInputStream *stream,
+                                         gssize                count,
+                                         int                   io_priority,
+                                         GCancellable         *cancellable,
+                                         GAsyncReadyCallback   callback,
+                                         gpointer              user_data)
+{
+  GBufferedInputStreamPrivate *priv;
+  GInputStream *base_stream;
+  GSimpleAsyncResult *simple;
+  gsize in_buffer;
+
+  priv = stream->priv;
+
+  if (count == -1)
+    count = priv->len;
+  
+  in_buffer = priv->end - priv->pos;
+
+  /* Never fill more than can fit in the buffer */
+  count = MIN (count, priv->len - in_buffer);
+
+  /* If requested length does not fit at end, compact */
+  if (priv->len - priv->end < count)
+    compact_buffer (stream);
+
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                     callback, user_data,
+                                     g_buffered_input_stream_real_fill_async);
+  
+  base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+  g_input_stream_read_async (base_stream,
+                            priv->buffer + priv->end,
+                            count,
+                            io_priority,
+                            cancellable,
+                            fill_async_callback,
+                            simple);
+}
+
+static gssize
+g_buffered_input_stream_real_fill_finish (GBufferedInputStream *stream,
+                                         GAsyncResult         *result,
+                                         GError              **error)
+{
+  GSimpleAsyncResult *simple;
+  gssize nread;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_buffered_input_stream_real_fill_async);
+  
+  nread = g_simple_async_result_get_op_res_gssize (simple);
+  return nread;
+}
+
+typedef struct {
+  gssize bytes_read;
+  gssize count;
+  void *buffer;
+} ReadAsyncData;
+
+static void 
+free_read_async_data (gpointer _data)
+{
+  ReadAsyncData *data = _data;
+  g_slice_free (ReadAsyncData, data);
+}
+
+static void
+large_read_callback (GObject *source_object,
+                    GAsyncResult *result,
+                    gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  ReadAsyncData *data;
+  GError *error;
+  gssize nread;
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  error = NULL;
+  nread = g_input_stream_read_finish (G_INPUT_STREAM (source_object),
+                                     result, &error);
+
+  /* Only report the error if we've not already read some data */
+  if (nread < 0 && data->bytes_read == 0)
+    g_simple_async_result_set_from_error (simple, error);
+  
+  if (nread > 0)
+    data->bytes_read += nread;
+  
+  if (error)
+    g_error_free (error);
+  
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+read_fill_buffer_callback (GObject *source_object,
+                          GAsyncResult *result,
+                          gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GBufferedInputStream *bstream;
+  GBufferedInputStreamPrivate *priv;
+  ReadAsyncData *data;
+  GError *error;
+  gssize nread;
+  gsize available;
+
+  bstream = G_BUFFERED_INPUT_STREAM (source_object);
+  priv = bstream->priv;
+  
+  g_input_stream_set_pending (G_INPUT_STREAM (bstream), TRUE); /* enable again */
+  
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  error = NULL;
+  nread = g_buffered_input_stream_fill_finish (bstream,
+                                              result, &error);
+  
+  if (nread < 0 && data->bytes_read == 0)
+    g_simple_async_result_set_from_error (simple, error);
+
+
+  if (nread > 0)
+    {
+      available = priv->end - priv->pos;
+      data->count = MIN (data->count, available);
+      
+      memcpy ((char *)data->buffer + data->bytes_read, (char *)priv->buffer + priv->pos, data->count);
+      data->bytes_read += data->count;
+      priv->pos += data->count;
+    }
+
+  if (error)
+    g_error_free (error);
+  
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+g_buffered_input_stream_read_async (GInputStream              *stream,
+                                    void                      *buffer,
+                                    gsize                      count,
+                                    int                        io_priority,
+                                    GCancellable              *cancellable,
+                                    GAsyncReadyCallback        callback,
+                                    gpointer                   user_data)
+{
+  GBufferedInputStream *bstream;
+  GBufferedInputStreamPrivate *priv;
+  GInputStream *base_stream;
+  gsize available;
+  GSimpleAsyncResult *simple;
+  ReadAsyncData *data;
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+  priv = bstream->priv;
+
+  data = g_slice_new (ReadAsyncData);
+  data->buffer = buffer;
+  data->bytes_read = 0;
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                     callback, user_data,
+                                     g_buffered_input_stream_read_async);
+  g_simple_async_result_set_op_res_gpointer (simple, data, free_read_async_data);
+  
+  available = priv->end - priv->pos;
+  
+  if (count <= available)
+    {
+      memcpy (buffer, priv->buffer + priv->pos, count);
+      priv->pos += count;
+      data->bytes_read = count;
+      
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+
+  /* Full request not available, read all currently availbile and request refill for more */
+  
+  memcpy (buffer, priv->buffer + priv->pos, available);
+  priv->pos = 0;
+  priv->end = 0;
+  
+  count -= available;
+  
+  data->bytes_read = available;
+  data->count = count;
+
+  if (count > priv->len)
+    {
+      /* Large request, shortcut buffer */
+      
+      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+      
+      g_input_stream_read_async (base_stream,
+                                (char *)buffer + data->bytes_read,
+                                count,
+                                io_priority, cancellable,
+                                large_read_callback,
+                                simple);
+    }
+  else
+    {
+      g_input_stream_set_pending (stream, FALSE); /* to avoid already pending error */
+      g_buffered_input_stream_fill_async (bstream, priv->len,
+                                         io_priority, cancellable,
+                                         read_fill_buffer_callback, simple);
+    }
+}
+
+static gssize
+g_buffered_input_stream_read_finish (GInputStream   *stream,
+                                     GAsyncResult   *result,
+                                     GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  ReadAsyncData *data;
+  
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_buffered_input_stream_read_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  return data->bytes_read;
+}
+
+typedef struct {
+  gssize bytes_skipped;
+  gssize count;
+} SkipAsyncData;
+
+static void 
+free_skip_async_data (gpointer _data)
+{
+  SkipAsyncData *data = _data;
+  g_slice_free (SkipAsyncData, data);
+}
+
+static void
+large_skip_callback (GObject *source_object,
+                    GAsyncResult *result,
+                    gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  SkipAsyncData *data;
+  GError *error;
+  gssize nread;
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  error = NULL;
+  nread = g_input_stream_skip_finish (G_INPUT_STREAM (source_object),
+                                     result, &error);
+
+  /* Only report the error if we've not already read some data */
+  if (nread < 0 && data->bytes_skipped == 0)
+    g_simple_async_result_set_from_error (simple, error);
+  
+  if (nread > 0)
+    data->bytes_skipped += nread;
+  
+  if (error)
+    g_error_free (error);
+  
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+skip_fill_buffer_callback (GObject *source_object,
+                          GAsyncResult *result,
+                          gpointer user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GBufferedInputStream *bstream;
+  GBufferedInputStreamPrivate *priv;
+  SkipAsyncData *data;
+  GError *error;
+  gssize nread;
+  gsize available;
+
+  bstream = G_BUFFERED_INPUT_STREAM (source_object);
+  priv = bstream->priv;
+  
+  g_input_stream_set_pending (G_INPUT_STREAM (bstream), TRUE); /* enable again */
+  
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  error = NULL;
+  nread = g_buffered_input_stream_fill_finish (bstream,
+                                              result, &error);
+  
+  if (nread < 0 && data->bytes_skipped == 0)
+    g_simple_async_result_set_from_error (simple, error);
+
+
+  if (nread > 0)
+    {
+      available = priv->end - priv->pos;
+      data->count = MIN (data->count, available);
+      
+      data->bytes_skipped += data->count;
+      priv->pos += data->count;
+    }
+
+  if (error)
+    g_error_free (error);
+  
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+}
+
+static void
+g_buffered_input_stream_skip_async (GInputStream              *stream,
+                                    gsize                      count,
+                                    int                        io_priority,
+                                    GCancellable              *cancellable,
+                                    GAsyncReadyCallback        callback,
+                                    gpointer                   user_data)
+{
+  GBufferedInputStream *bstream;
+  GBufferedInputStreamPrivate *priv;
+  GInputStream *base_stream;
+  gsize available;
+  GSimpleAsyncResult *simple;
+  SkipAsyncData *data;
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+  priv = bstream->priv;
+
+  data = g_slice_new (SkipAsyncData);
+  data->bytes_skipped = 0;
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                     callback, user_data,
+                                     g_buffered_input_stream_skip_async);
+  g_simple_async_result_set_op_res_gpointer (simple, data, free_skip_async_data);
+  
+  available = priv->end - priv->pos;
+  
+  if (count <= available)
+    {
+      priv->pos += count;
+      data->bytes_skipped = count;
+      
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+
+  /* Full request not available, skip all currently availbile and request refill for more */
+  
+  priv->pos = 0;
+  priv->end = 0;
+  
+  count -= available;
+  
+  data->bytes_skipped = available;
+  data->count = count;
+
+  if (count > priv->len)
+    {
+      /* Large request, shortcut buffer */
+      
+      base_stream = G_FILTER_INPUT_STREAM (stream)->base_stream;
+      
+      g_input_stream_skip_async (base_stream,
+                                count,
+                                io_priority, cancellable,
+                                large_skip_callback,
+                                simple);
+    }
+  else
+    {
+      g_input_stream_set_pending (stream, FALSE); /* to avoid already pending error */
+      g_buffered_input_stream_fill_async (bstream, priv->len,
+                                         io_priority, cancellable,
+                                         skip_fill_buffer_callback, simple);
+    }
+}
+
+static gssize
+g_buffered_input_stream_skip_finish (GInputStream   *stream,
+                                     GAsyncResult   *result,
+                                     GError        **error)
+{
+  GSimpleAsyncResult *simple;
+  SkipAsyncData *data;
+  
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_buffered_input_stream_skip_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  
+  return data->bytes_skipped;
+}
+
+/* vim: ts=2 sw=2 et */
diff --git a/gio/gbufferedinputstream.h b/gio/gbufferedinputstream.h
new file mode 100644 (file)
index 0000000..eab35b5
--- /dev/null
@@ -0,0 +1,110 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#ifndef __G_BUFFERED_INPUT_STREAM_H__
+#define __G_BUFFERED_INPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gfilterinputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_BUFFERED_INPUT_STREAM         (g_buffered_input_stream_get_type ())
+#define G_BUFFERED_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStream))
+#define G_BUFFERED_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStreamClass))
+#define G_IS_BUFFERED_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUFFERED_INPUT_STREAM))
+#define G_IS_BUFFERED_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUFFERED_INPUT_STREAM))
+#define G_BUFFERED_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUFFERED_INPUT_STREAM, GBufferedInputStreamClass))
+
+typedef struct _GBufferedInputStream         GBufferedInputStream;
+typedef struct _GBufferedInputStreamClass    GBufferedInputStreamClass;
+typedef struct _GBufferedInputStreamPrivate  GBufferedInputStreamPrivate;
+
+struct _GBufferedInputStream
+{
+  GFilterInputStream parent;
+
+  /*< private >*/
+  GBufferedInputStreamPrivate *priv;
+};
+
+struct _GBufferedInputStreamClass
+{
+ GFilterInputStreamClass parent_class;
+
+  gssize   (* fill)        (GBufferedInputStream *stream,
+                           gssize                count,
+                           GCancellable         *cancellable,
+                           GError              **error);
+
+  /* Async ops: (optional in derived classes) */
+  void     (* fill_async)  (GBufferedInputStream *stream,
+                           gssize                count,
+                           int                   io_priority,
+                           GCancellable         *cancellable,
+                           GAsyncReadyCallback   callback,
+                           gpointer              user_data);
+  gssize   (* fill_finish) (GBufferedInputStream *stream,
+                           GAsyncResult         *result,
+                           GError              **error);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+
+GType         g_buffered_input_stream_get_type        (void) G_GNUC_CONST;
+GInputStream* g_buffered_input_stream_new             (GInputStream          *base_stream);
+GInputStream* g_buffered_input_stream_new_sized       (GInputStream          *base_stream,
+                                                      gsize                  size);
+
+gsize         g_buffered_input_stream_get_buffer_size (GBufferedInputStream  *stream);
+void          g_buffered_input_stream_set_buffer_size (GBufferedInputStream  *stream,
+                                                      gsize                  size);
+gsize         g_buffered_input_stream_get_available   (GBufferedInputStream  *stream);
+gsize         g_buffered_input_stream_peek            (GBufferedInputStream  *stream,
+                                                      void                  *buffer,
+                                                      gsize                  offset,
+                                                      gsize                  count);
+
+gssize        g_buffered_input_stream_fill            (GBufferedInputStream  *stream,
+                                                      gssize                 count,
+                                                      GCancellable          *cancellable,
+                                                      GError               **error);
+void          g_buffered_input_stream_fill_async      (GBufferedInputStream  *stream,
+                                                      gssize                 count,
+                                                      int                    io_priority,
+                                                      GCancellable          *cancellable,
+                                                      GAsyncReadyCallback    callback,
+                                                      gpointer               user_data);
+gssize        g_buffered_input_stream_fill_finish     (GBufferedInputStream  *stream,
+                                                      GAsyncResult          *result,
+                                                      GError               **error);
+
+
+G_END_DECLS
+
+#endif /* __G_BUFFERED_INPUT_STREAM_H__ */
diff --git a/gio/gbufferedoutputstream.c b/gio/gbufferedoutputstream.c
new file mode 100644 (file)
index 0000000..210ccfb
--- /dev/null
@@ -0,0 +1,734 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#include <config.h>
+
+#include "gbufferedoutputstream.h"
+#include "goutputstream.h"
+#include "gsimpleasyncresult.h"
+#include "string.h"
+
+#include "glibintl.h"
+
+#define DEFAULT_BUFFER_SIZE 4096
+
+struct _GBufferedOutputStreamPrivate {
+  guint8 *buffer; 
+  gsize   len;
+  goffset pos;
+  gboolean auto_grow;
+};
+
+enum {
+  PROP_0,
+  PROP_BUFSIZE
+};
+
+static void     g_buffered_output_stream_set_property (GObject      *object,
+                                                       guint         prop_id,
+                                                       const GValue *value,
+                                                       GParamSpec   *pspec);
+
+static void     g_buffered_output_stream_get_property (GObject    *object,
+                                                       guint       prop_id,
+                                                       GValue     *value,
+                                                       GParamSpec *pspec);
+static void     g_buffered_output_stream_finalize     (GObject *object);
+
+
+static gssize   g_buffered_output_stream_write        (GOutputStream *stream,
+                                                       const void    *buffer,
+                                                       gsize          count,
+                                                       GCancellable  *cancellable,
+                                                       GError       **error);
+static gboolean g_buffered_output_stream_flush        (GOutputStream    *stream,
+                                                       GCancellable  *cancellable,
+                                                       GError          **error);
+static gboolean g_buffered_output_stream_close        (GOutputStream  *stream,
+                                                       GCancellable   *cancellable,
+                                                       GError        **error);
+
+static void     g_buffered_output_stream_write_async  (GOutputStream        *stream,
+                                                       const void           *buffer,
+                                                       gsize                 count,
+                                                       int                   io_priority,
+                                                       GCancellable         *cancellable,
+                                                       GAsyncReadyCallback   callback,
+                                                       gpointer              data);
+static gssize   g_buffered_output_stream_write_finish (GOutputStream        *stream,
+                                                       GAsyncResult         *result,
+                                                       GError              **error);
+static void     g_buffered_output_stream_flush_async  (GOutputStream        *stream,
+                                                       int                   io_priority,
+                                                       GCancellable         *cancellable,
+                                                       GAsyncReadyCallback   callback,
+                                                       gpointer              data);
+static gboolean g_buffered_output_stream_flush_finish (GOutputStream        *stream,
+                                                       GAsyncResult         *result,
+                                                       GError              **error);
+static void     g_buffered_output_stream_close_async  (GOutputStream        *stream,
+                                                       int                   io_priority,
+                                                       GCancellable         *cancellable,
+                                                       GAsyncReadyCallback   callback,
+                                                       gpointer              data);
+static gboolean g_buffered_output_stream_close_finish (GOutputStream        *stream,
+                                                       GAsyncResult         *result,
+                                                       GError              **error);
+
+G_DEFINE_TYPE (GBufferedOutputStream,
+               g_buffered_output_stream,
+               G_TYPE_FILTER_OUTPUT_STREAM)
+
+
+static void
+g_buffered_output_stream_class_init (GBufferedOutputStreamClass *klass)
+{
+  GObjectClass *object_class;
+  GOutputStreamClass *ostream_class;
+  g_type_class_add_private (klass, sizeof (GBufferedOutputStreamPrivate));
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = g_buffered_output_stream_get_property;
+  object_class->set_property = g_buffered_output_stream_set_property;
+  object_class->finalize     = g_buffered_output_stream_finalize;
+
+  ostream_class = G_OUTPUT_STREAM_CLASS (klass);
+  ostream_class->write = g_buffered_output_stream_write;
+  ostream_class->flush = g_buffered_output_stream_flush;
+  ostream_class->close = g_buffered_output_stream_close;
+  ostream_class->write_async  = g_buffered_output_stream_write_async;
+  ostream_class->write_finish = g_buffered_output_stream_write_finish;
+  ostream_class->flush_async  = g_buffered_output_stream_flush_async;
+  ostream_class->flush_finish = g_buffered_output_stream_flush_finish;
+  ostream_class->close_async  = g_buffered_output_stream_close_async;
+  ostream_class->close_finish = g_buffered_output_stream_close_finish;
+
+  g_object_class_install_property (object_class,
+                                   PROP_BUFSIZE,
+                                   g_param_spec_uint ("buffer-size",
+                                                      P_("Buffer Size"),
+                                                      P_("The size of the backend buffer"),
+                                                      1,
+                                                      G_MAXUINT,
+                                                      DEFAULT_BUFFER_SIZE,
+                                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+}
+
+/**
+ * g_buffered_output_stream_get_buffer_size:
+ * @stream: a #GBufferedOutputStream.
+ * 
+ * Returns: the current size of the buffer.
+ **/
+gsize
+g_buffered_output_stream_get_buffer_size (GBufferedOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream), -1);
+
+  return stream->priv->len;
+}
+
+/**
+ * g_buffered_output_stream_set_buffer_size:
+ * @stream: a #GBufferedOutputStream.
+ * @size: a #gsize.
+ *
+ * Sets the size of the internal buffer to @size.
+ * 
+ **/    
+void
+g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
+                                        gsize                 size)
+{
+  GBufferedOutputStreamPrivate *priv;
+  guint8 *buffer;
+  
+  g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
+
+  priv = stream->priv;
+  
+  if (priv->buffer)
+    {
+      size = MAX (size, priv->pos);
+
+      buffer = g_malloc (size);
+      memcpy (buffer, priv->buffer, priv->pos);
+      g_free (priv->buffer);
+      priv->buffer = buffer;
+      priv->len = size;
+      /* Keep old pos */
+    }
+  else
+    {
+      priv->buffer = g_malloc (size);
+      priv->len = size;
+      priv->pos = 0;
+    }
+}
+
+/**
+ * g_buffered_output_stream_get_auto_grow:
+ * @stream: a #GBufferedOutputStream.
+ * 
+ * Returns: %TRUE if the @stream's buffer automatically grows,
+ * %FALSE otherwise.
+ **/  
+gboolean
+g_buffered_output_stream_get_auto_grow (GBufferedOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream), FALSE);
+
+  return stream->priv->auto_grow;
+}
+
+/**
+ * g_buffered_output_stream_set_auto_grow:
+ * @stream: a #GBufferedOutputStream.
+ * @auto_grow: a boolean.
+ *
+ * Sets whether or not the @stream's buffer should automatically grow.
+ * 
+ **/
+void
+g_buffered_output_stream_set_auto_grow (GBufferedOutputStream *stream,
+                                      gboolean              auto_grow)
+{
+  g_return_if_fail (G_IS_BUFFERED_OUTPUT_STREAM (stream));
+
+  stream->priv->auto_grow = auto_grow;
+}
+
+static void
+g_buffered_output_stream_set_property (GObject         *object,
+                                       guint            prop_id,
+                                       const GValue    *value,
+                                       GParamSpec      *pspec)
+{
+  GBufferedOutputStream *buffered_stream;
+  GBufferedOutputStreamPrivate *priv;
+
+  buffered_stream = G_BUFFERED_OUTPUT_STREAM (object);
+  priv = buffered_stream->priv;
+
+  switch (prop_id) 
+    {
+
+    case PROP_BUFSIZE:
+      g_buffered_output_stream_set_buffer_size (buffered_stream, g_value_get_uint (value));
+      break;    
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_buffered_output_stream_get_property (GObject    *object,
+                                       guint       prop_id,
+                                       GValue     *value,
+                                       GParamSpec *pspec)
+{
+  GBufferedOutputStream *buffered_stream;
+  GBufferedOutputStreamPrivate *priv;
+
+  buffered_stream = G_BUFFERED_OUTPUT_STREAM (object);
+  priv = buffered_stream->priv;
+
+  switch (prop_id)
+    {
+
+    case PROP_BUFSIZE:
+      g_value_set_uint (value, priv->len);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_buffered_output_stream_finalize (GObject *object)
+{
+  GBufferedOutputStream *stream;
+  GBufferedOutputStreamPrivate *priv;
+
+  stream = G_BUFFERED_OUTPUT_STREAM (object);
+  priv = stream->priv;
+
+  g_free (priv->buffer);
+
+  if (G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_buffered_output_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_buffered_output_stream_init (GBufferedOutputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                              G_TYPE_BUFFERED_OUTPUT_STREAM,
+                                              GBufferedOutputStreamPrivate);
+
+}
+
+/**
+ * g_buffered_output_stream_new:
+ * @base_stream: a #GOutputStream.
+ * 
+ * Returns: a #GOutputStream for the given @base_stream.
+ **/  
+GOutputStream *
+g_buffered_output_stream_new (GOutputStream *base_stream)
+{
+  GOutputStream *stream;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
+
+  stream = g_object_new (G_TYPE_BUFFERED_OUTPUT_STREAM,
+                         "base-stream", base_stream,
+                         NULL);
+
+  return stream;
+}
+
+/**
+ * g_buffered_output_stream_new_sized:
+ * @base_stream: a #GOutputStream.
+ * @size: a #gsize.
+ * 
+ * Returns: a #GOutputStream with an internal buffer set to @size.
+ **/  
+GOutputStream *
+g_buffered_output_stream_new_sized (GOutputStream *base_stream,
+                                    guint          size)
+{
+  GOutputStream *stream;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
+
+  stream = g_object_new (G_TYPE_BUFFERED_OUTPUT_STREAM,
+                         "base-stream", base_stream,
+                         "buffer-size", size,
+                         NULL);
+
+  return stream;
+}
+
+static gboolean
+flush_buffer (GBufferedOutputStream  *stream,
+              GCancellable           *cancellable,
+              GError                 **error)
+{
+  GBufferedOutputStreamPrivate *priv;
+  GOutputStream                *base_stream;
+  gboolean                      res;
+  gsize                         bytes_written;
+  gsize                         count;
+
+  priv = stream->priv;
+  bytes_written = 0;
+  base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), FALSE);
+
+  res = g_output_stream_write_all (base_stream,
+                                   priv->buffer,
+                                   priv->pos,
+                                   &bytes_written,
+                                   cancellable,
+                                   error);
+
+  count = priv->pos - bytes_written;
+
+  if (count > 0)
+    g_memmove (priv->buffer, priv->buffer + bytes_written, count);
+  
+  priv->pos -= bytes_written;
+
+  return res;
+}
+
+static gssize
+g_buffered_output_stream_write  (GOutputStream *stream,
+                                 const void    *buffer,
+                                 gsize          count,
+                                 GCancellable  *cancellable,
+                                 GError       **error)
+{
+  GBufferedOutputStream        *bstream;
+  GBufferedOutputStreamPrivate *priv;
+  gboolean res;
+  gsize    n;
+  gsize new_size;
+
+  bstream = G_BUFFERED_OUTPUT_STREAM (stream);
+  priv = bstream->priv;
+
+  n = priv->len - priv->pos;
+
+  if (priv->auto_grow && n < count)
+    {
+      new_size = MAX (priv->len * 2, priv->len + count);
+      g_buffered_output_stream_set_buffer_size (bstream, new_size);
+    }
+  else if (n == 0)
+    {
+      res = flush_buffer (bstream, cancellable, error);
+      
+      if (res == FALSE)
+       return -1;
+    }
+
+  n = priv->len - priv->pos;
+  
+  count = MIN (count, n);
+  memcpy (priv->buffer + priv->pos, buffer, count);
+  priv->pos += count;
+
+  return count;
+}
+
+static gboolean
+g_buffered_output_stream_flush (GOutputStream  *stream,
+                                GCancellable   *cancellable,
+                                GError        **error)
+{
+  GBufferedOutputStream *bstream;
+  GBufferedOutputStreamPrivate *priv;
+  GOutputStream                *base_stream;
+  gboolean res;
+
+  bstream = G_BUFFERED_OUTPUT_STREAM (stream);
+  priv = bstream->priv;
+  base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
+
+  res = flush_buffer (bstream, cancellable, error);
+
+  if (res == FALSE) {
+    return FALSE;
+  }
+
+  res = g_output_stream_flush (base_stream,
+                               cancellable,
+                               error);
+  return res;
+}
+
+static gboolean
+g_buffered_output_stream_close (GOutputStream  *stream,
+                                GCancellable   *cancellable,
+                                GError        **error)
+{
+  GBufferedOutputStream        *bstream;
+  GBufferedOutputStreamPrivate *priv;
+  GOutputStream                *base_stream;
+  gboolean                      res;
+
+  bstream = G_BUFFERED_OUTPUT_STREAM (stream);
+  priv = bstream->priv;
+  base_stream = G_FILTER_OUTPUT_STREAM (bstream)->base_stream;
+
+  res = flush_buffer (bstream, cancellable, error);
+
+  /* report the first error but still close the stream */
+  if (res)
+    {
+      res = g_output_stream_close (base_stream,
+                                   cancellable,
+                                   error); 
+    }
+  else
+    {
+      g_output_stream_close (base_stream,
+                             cancellable,
+                             NULL); 
+    }
+
+  return res;
+}
+
+/* ************************** */
+/* Async stuff implementation */
+/* ************************** */
+
+/* TODO: This should be using the base class async ops, not threads */
+
+typedef struct {
+
+  guint flush_stream : 1;
+  guint close_stream : 1;
+
+} FlushData;
+
+static void
+free_flush_data (gpointer data)
+{
+  g_slice_free (FlushData, data);
+}
+
+/* This function is used by all three (i.e. 
+ * _write, _flush, _close) functions since
+ * all of them will need to flush the buffer
+ * and so closing and writing is just a special
+ * case of flushing + some addition stuff */
+static void
+flush_buffer_thread (GSimpleAsyncResult *result,
+                     GObject            *object,
+                     GCancellable       *cancellable)
+{
+  GBufferedOutputStream *stream;
+  GOutputStream *base_stream;
+  FlushData     *fdata;
+  gboolean       res;
+  GError        *error = NULL;
+
+  stream = G_BUFFERED_OUTPUT_STREAM (object);
+  fdata = g_simple_async_result_get_op_res_gpointer (result);
+  base_stream = G_FILTER_OUTPUT_STREAM (stream)->base_stream;
+
+  res = flush_buffer (stream, cancellable, &error);
+
+  /* if flushing the buffer didn't work don't even bother
+   * to flush the stream but just report that error */
+  if (res && fdata->flush_stream)
+    {
+      res = g_output_stream_flush (base_stream,
+                                   cancellable,
+                                   &error);
+    }
+
+  if (fdata->close_stream) 
+    {
+     
+      /* if flushing the buffer or the stream returned 
+       * an error report that first error but still try 
+       * close the stream */
+      if (res == FALSE)
+        {
+          g_output_stream_close (base_stream,
+                                 cancellable,
+                                 NULL);
+        } 
+      else 
+        {
+          res = g_output_stream_close (base_stream,
+                                       cancellable,
+                                       &error);
+        } 
+
+    }
+
+  if (res == FALSE)
+    {
+      g_simple_async_result_set_from_error (result, error);
+      g_error_free (error);
+    }
+}
+
+typedef struct {
+    
+  FlushData fdata;
+
+  gsize  count;
+  const void  *buffer;
+
+} WriteData;
+
+static void 
+free_write_data (gpointer data)
+{
+  g_slice_free (WriteData, data);
+}
+
+static void
+g_buffered_output_stream_write_async (GOutputStream        *stream,
+                                      const void           *buffer,
+                                      gsize                 count,
+                                      int                   io_priority,
+                                      GCancellable         *cancellable,
+                                      GAsyncReadyCallback   callback,
+                                      gpointer              data)
+{
+  GBufferedOutputStream *buffered_stream;
+  GBufferedOutputStreamPrivate *priv;
+  GSimpleAsyncResult *res;
+  WriteData *wdata;
+
+  buffered_stream = G_BUFFERED_OUTPUT_STREAM (stream);
+  priv = buffered_stream->priv;
+
+  wdata = g_slice_new (WriteData);
+  wdata->count  = count;
+  wdata->buffer = buffer;
+
+  res = g_simple_async_result_new (G_OBJECT (stream),
+                                   callback,
+                                   data,
+                                   g_buffered_output_stream_write_async);
+
+  g_simple_async_result_set_op_res_gpointer (res, wdata, free_write_data);
+
+  /* if we have space left directly call the
+   * callback (from idle) otherwise schedule a buffer 
+   * flush in the thread. In both cases the actual
+   * copying of the data to the buffer will be done in
+   * the write_finish () func since that should
+   * be fast enough */
+  if (priv->len - priv->pos > 0)
+    {
+      g_simple_async_result_complete_in_idle (res);
+    }
+  else
+    {
+      wdata->fdata.flush_stream = FALSE;
+      wdata->fdata.close_stream = FALSE;
+      g_simple_async_result_run_in_thread (res, 
+                                           flush_buffer_thread, 
+                                           io_priority,
+                                           cancellable);
+      g_object_unref (res);
+    }
+}
+
+static gssize
+g_buffered_output_stream_write_finish (GOutputStream        *stream,
+                                       GAsyncResult         *result,
+                                       GError              **error)
+{
+  GBufferedOutputStreamPrivate *priv;
+  GBufferedOutputStream        *buffered_stream;
+  GSimpleAsyncResult *simple;
+  WriteData          *wdata;
+  gssize              count;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  buffered_stream = G_BUFFERED_OUTPUT_STREAM (stream);
+  priv = buffered_stream->priv;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+            g_buffered_output_stream_write_async);
+
+  wdata = g_simple_async_result_get_op_res_gpointer (simple);
+
+  /* Now do the real copying of data to the buffer */
+  count = priv->len - priv->pos; 
+  count = MIN (wdata->count, count);
+
+  memcpy (priv->buffer + priv->pos, wdata->buffer, count);
+  
+  priv->pos += count;
+
+  return count;
+}
+
+static void
+g_buffered_output_stream_flush_async (GOutputStream        *stream,
+                                      int                   io_priority,
+                                      GCancellable         *cancellable,
+                                      GAsyncReadyCallback   callback,
+                                      gpointer              data)
+{
+  GSimpleAsyncResult *res;
+  FlushData          *fdata;
+
+  fdata = g_slice_new (FlushData);
+  fdata->flush_stream = TRUE;
+  fdata->close_stream = FALSE;
+
+  res = g_simple_async_result_new (G_OBJECT (stream),
+                                   callback,
+                                   data,
+                                   g_buffered_output_stream_flush_async);
+
+  g_simple_async_result_set_op_res_gpointer (res, fdata, free_flush_data);
+
+  g_simple_async_result_run_in_thread (res, 
+                                       flush_buffer_thread, 
+                                       io_priority,
+                                       cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_buffered_output_stream_flush_finish (GOutputStream        *stream,
+                                       GAsyncResult         *result,
+                                       GError              **error)
+{
+  GSimpleAsyncResult *simple;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+            g_buffered_output_stream_flush_async);
+
+  return TRUE;
+}
+
+static void
+g_buffered_output_stream_close_async (GOutputStream        *stream,
+                                      int                   io_priority,
+                                      GCancellable         *cancellable,
+                                      GAsyncReadyCallback   callback,
+                                      gpointer              data)
+{
+  GSimpleAsyncResult *res;
+  FlushData          *fdata;
+
+  fdata = g_slice_new (FlushData);
+  fdata->close_stream = TRUE;
+
+  res = g_simple_async_result_new (G_OBJECT (stream),
+                                   callback,
+                                   data,
+                                   g_buffered_output_stream_close_async);
+
+  g_simple_async_result_set_op_res_gpointer (res, fdata, free_flush_data);
+
+  g_simple_async_result_run_in_thread (res, 
+                                       flush_buffer_thread, 
+                                       io_priority,
+                                       cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_buffered_output_stream_close_finish (GOutputStream        *stream,
+                                       GAsyncResult         *result,
+                                       GError              **error)
+{
+  GSimpleAsyncResult *simple;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+            g_buffered_output_stream_flush_async);
+
+  return TRUE;
+}
+
+/* vim: ts=2 sw=2 et */
diff --git a/gio/gbufferedoutputstream.h b/gio/gbufferedoutputstream.h
new file mode 100644 (file)
index 0000000..5408644
--- /dev/null
@@ -0,0 +1,76 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#ifndef __G_BUFFERED_OUTPUT_STREAM_H__
+#define __G_BUFFERED_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gfilteroutputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_BUFFERED_OUTPUT_STREAM         (g_buffered_output_stream_get_type ())
+#define G_BUFFERED_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStream))
+#define G_BUFFERED_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStreamClass))
+#define G_IS_BUFFERED_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUFFERED_OUTPUT_STREAM))
+#define G_IS_BUFFERED_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUFFERED_OUTPUT_STREAM))
+#define G_BUFFERED_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUFFERED_OUTPUT_STREAM, GBufferedOutputStreamClass))
+
+typedef struct _GBufferedOutputStream         GBufferedOutputStream;
+typedef struct _GBufferedOutputStreamClass    GBufferedOutputStreamClass;
+typedef struct _GBufferedOutputStreamPrivate  GBufferedOutputStreamPrivate;
+
+struct _GBufferedOutputStream
+{
+  GFilterOutputStream parent;
+
+  /*< protected >*/
+  GBufferedOutputStreamPrivate *priv;
+};
+
+struct _GBufferedOutputStreamClass
+{
+ GOutputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+
+GType           g_buffered_output_stream_get_type  (void) G_GNUC_CONST;
+GOutputStream* g_buffered_output_stream_new             (GOutputStream         *base_stream);
+GOutputStream* g_buffered_output_stream_new_sized       (GOutputStream         *base_stream,
+                                                        guint                  size);
+gsize          g_buffered_output_stream_get_buffer_size (GBufferedOutputStream *stream);
+void           g_buffered_output_stream_set_buffer_size (GBufferedOutputStream *stream,
+                                                        gsize                  size);
+gboolean       g_buffered_output_stream_get_auto_grow   (GBufferedOutputStream *stream);
+void           g_buffered_output_stream_set_auto_grow   (GBufferedOutputStream *stream,
+                                                        gboolean               auto_grow);
+
+G_END_DECLS
+
+#endif /* __G_BUFFERED_OUTPUT_STREAM_H__ */
diff --git a/gio/gcancellable.c b/gio/gcancellable.c
new file mode 100644 (file)
index 0000000..c55737a
--- /dev/null
@@ -0,0 +1,322 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <gioerror.h>
+#include "gcancellable.h"
+
+#include "glibintl.h"
+
+
+/*
+ * GCancellable is a thread-safe operation cancellation stack used 
+ * throughout GIO to allow for cancellation of asynchronous operations.
+ */
+
+enum {
+  CANCELLED,
+  LAST_SIGNAL
+};
+
+struct _GCancellable
+{
+  GObject parent_instance;
+
+  guint cancelled : 1;
+  guint allocated_pipe : 1;
+  int cancel_pipe[2];
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GCancellable, g_cancellable, G_TYPE_OBJECT);
+
+static GStaticPrivate current_cancellable = G_STATIC_PRIVATE_INIT;
+G_LOCK_DEFINE_STATIC(cancellable);
+  
+static void
+g_cancellable_finalize (GObject *object)
+{
+  GCancellable *cancellable = G_CANCELLABLE (object);
+
+  if (cancellable->cancel_pipe[0] != -1)
+    close (cancellable->cancel_pipe[0]);
+  
+  if (cancellable->cancel_pipe[1] != -1)
+    close (cancellable->cancel_pipe[1]);
+  
+  if (G_OBJECT_CLASS (g_cancellable_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_cancellable_parent_class)->finalize) (object);
+}
+
+static void
+g_cancellable_class_init (GCancellableClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_cancellable_finalize;
+
+  signals[CANCELLED] =
+    g_signal_new (I_("cancelled"),
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GCancellableClass, cancelled),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+  
+}
+
+static void
+set_fd_nonblocking (int fd)
+{
+#ifdef F_GETFL
+  glong fcntl_flags;
+  fcntl_flags = fcntl (fd, F_GETFL);
+
+#ifdef O_NONBLOCK
+  fcntl_flags |= O_NONBLOCK;
+#else
+  fcntl_flags |= O_NDELAY;
+#endif
+
+  fcntl (fd, F_SETFL, fcntl_flags);
+#endif
+}
+
+static void
+g_cancellable_open_pipe (GCancellable *cancellable)
+{
+  if (pipe (cancellable->cancel_pipe) == 0)
+    {
+      /* Make them nonblocking, just to be sure we don't block
+       * on errors and stuff
+       */
+      set_fd_nonblocking (cancellable->cancel_pipe[0]);
+      set_fd_nonblocking (cancellable->cancel_pipe[1]);
+    }
+  else
+    g_warning ("Failed to create pipe for GCancellable. Out of file descriptors?");
+}
+
+static void
+g_cancellable_init (GCancellable *cancellable)
+{
+  cancellable->cancel_pipe[0] = -1;
+  cancellable->cancel_pipe[1] = -1;
+}
+
+/**
+ * g_cancellable_new:
+ *  
+ * Returns: a new #GCancellable object.
+ **/
+GCancellable *
+g_cancellable_new (void)
+{
+  return g_object_new (G_TYPE_CANCELLABLE, NULL);
+}
+
+/**
+ * g_push_current_cancellable:
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * 
+ * Pushes @cancellable onto the cancellable stack.
+ **/
+void
+g_push_current_cancellable (GCancellable *cancellable)
+{
+  GSList *l;
+
+  g_assert (cancellable != NULL);
+  
+  l = g_static_private_get (&current_cancellable);
+  l = g_slist_prepend (l, cancellable);
+  g_static_private_set (&current_cancellable, l, NULL);
+}
+
+/**
+ * g_pop_current_cancellable:
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ *
+ * Pops @cancellable off the cancellable stack if @cancellable 
+ * is on the top of the stack.
+ **/
+void
+g_pop_current_cancellable (GCancellable *cancellable)
+{
+  GSList *l;
+  
+  l = g_static_private_get (&current_cancellable);
+  
+  g_assert (l != NULL);
+  g_assert (l->data == cancellable);
+
+  l = g_slist_delete_link (l, l);
+  g_static_private_set (&current_cancellable, l, NULL);
+}
+
+/**
+ * g_cancellable_get_current:
+ * 
+ * Returns: a #GCancellable from the top of the stack, or %NULL
+ * if the stack is empty. 
+ **/
+GCancellable *
+g_cancellable_get_current  (void)
+{
+  GSList *l;
+  
+  l = g_static_private_get (&current_cancellable);
+  if (l == NULL)
+    return NULL;
+
+  return G_CANCELLABLE (l->data);
+}
+
+/**
+ * g_cancellable_reset:
+ * @cancellable: a #GCancellable object.
+ * 
+ * Resets @cancellable to its uncancelled state. 
+ *
+ **/
+void 
+g_cancellable_reset (GCancellable *cancellable)
+{
+  g_return_if_fail (G_IS_CANCELLABLE (cancellable));
+
+  G_LOCK(cancellable);
+  /* Make sure we're not leaving old cancel state around */
+  if (cancellable->cancelled)
+    {
+      char ch;
+      if (cancellable->cancel_pipe[0] != -1)
+       read (cancellable->cancel_pipe[0], &ch, 1);
+      cancellable->cancelled = FALSE;
+    }
+  G_UNLOCK(cancellable);
+}
+
+/**
+ * g_cancellable_is_cancelled:
+ * @cancellable: a #GCancellable or NULL.
+ * 
+ * Returns: %TRUE if @cancellable is is cancelled, 
+ * FALSE if called with %NULL or if item is not cancelled. 
+ **/
+gboolean
+g_cancellable_is_cancelled (GCancellable *cancellable)
+{
+  return cancellable != NULL && cancellable->cancelled;
+}
+
+/**
+ * g_cancellable_set_error_if_cancelled:
+ * @cancellable: a #GCancellable object.
+ * @error: #GError to append error state to.
+ * 
+ * Sets the current error to notify that the operation was cancelled.
+ * 
+ * Returns: %TRUE if @cancellable was cancelled, %FALSE if it was not.
+ **/
+gboolean
+g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
+                                     GError       **error)
+{
+  if (g_cancellable_is_cancelled (cancellable))
+    {
+      g_set_error (error,
+                  G_IO_ERROR,
+                  G_IO_ERROR_CANCELLED,
+                  _("Operation was cancelled"));
+      return TRUE;
+    }
+  
+  return FALSE;
+}
+
+/**
+ * g_cancellable_get_fd:
+ * @cancellable: a #GCancellable.
+ * 
+ * Returns: A valid file descriptor. -1 if the file descriptor 
+ * is not supported, or on errors. 
+ **/
+int
+g_cancellable_get_fd (GCancellable *cancellable)
+{
+  int fd;
+  if (cancellable == NULL)
+    return -1;
+  
+  G_LOCK(cancellable);
+  if (!cancellable->allocated_pipe)
+    {
+      cancellable->allocated_pipe = TRUE;
+      g_cancellable_open_pipe (cancellable);
+    }
+  
+  fd = cancellable->cancel_pipe[0];
+  G_UNLOCK(cancellable);
+  
+  return fd;
+}
+
+/**
+ * g_cancellable_cancel:
+ * @cancellable: a #GCancellable object.
+ * 
+ * Will set @cancellable to cancelled, and will emit the CANCELLED
+ * signal. This function is thread-safe.
+ *  
+ **/
+void
+g_cancellable_cancel (GCancellable *cancellable)
+{
+  gboolean cancel;
+
+  cancel = FALSE;
+  
+  G_LOCK(cancellable);
+  if (cancellable != NULL &&
+      !cancellable->cancelled)
+    {
+      char ch = 'x';
+      cancel = TRUE;
+      cancellable->cancelled = TRUE;
+      if (cancellable->cancel_pipe[1] != -1)
+       write (cancellable->cancel_pipe[1], &ch, 1);
+    }
+  G_UNLOCK(cancellable);
+
+  if (cancel)
+    {
+      g_object_ref (cancellable);
+      g_signal_emit (cancellable, signals[CANCELLED], 0);
+      g_object_unref (cancellable);
+    }
+}
+
+
diff --git a/gio/gcancellable.h b/gio/gcancellable.h
new file mode 100644 (file)
index 0000000..a6229da
--- /dev/null
@@ -0,0 +1,74 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_CANCELLABLE_H__
+#define __G_CANCELLABLE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_CANCELLABLE         (g_cancellable_get_type ())
+#define G_CANCELLABLE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_CANCELLABLE, GCancellable))
+#define G_CANCELLABLE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_CANCELLABLE, GCancellableClass))
+#define G_IS_CANCELLABLE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_CANCELLABLE))
+#define G_IS_CANCELLABLE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_CANCELLABLE))
+#define G_CANCELLABLE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_CANCELLABLE, GCancellableClass))
+
+typedef struct _GCancellable        GCancellable;
+typedef struct _GCancellableClass   GCancellableClass;
+
+struct _GCancellableClass
+{
+  GObjectClass parent_class;
+
+  void (* cancelled) (GCancellable *cancellable);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_cancellable_get_type (void) G_GNUC_CONST;
+
+GCancellable *g_cancellable_new          (void);
+
+/* These are only safe to call inside a cancellable op */
+gboolean      g_cancellable_is_cancelled           (GCancellable  *cancellable);
+gboolean      g_cancellable_set_error_if_cancelled (GCancellable  *cancellable,
+                                                   GError       **error);
+int           g_cancellable_get_fd                 (GCancellable  *cancellable);
+GCancellable *g_cancellable_get_current            (void);
+void          g_push_current_cancellable           (GCancellable  *cancellable);
+void          g_pop_current_cancellable            (GCancellable  *cancellable);
+void          g_cancellable_reset                  (GCancellable  *cancellable);
+
+
+/* This is safe to call from another thread */
+void          g_cancellable_cancel       (GCancellable  *cancellable);
+
+G_END_DECLS
+
+#endif /* __G_CANCELLABLE_H__ */
diff --git a/gio/gcontenttype.c b/gio/gcontenttype.c
new file mode 100644 (file)
index 0000000..5952cac
--- /dev/null
@@ -0,0 +1,861 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "gcontenttypeprivate.h"
+#include "glibintl.h"
+
+/* A content type is a platform specific string that defines the type
+   of a file. On unix it is a mime type, on win32 it is an extension string
+   like ".doc", ".txt" or a percieved string like "audio". Such strings
+   can be looked up in the registry at HKEY_CLASSES_ROOT.
+*/
+
+#ifdef G_OS_WIN32
+
+#include <windows.h>
+
+static char *
+get_registry_classes_key (const char *subdir,
+                         const wchar_t *key_name)
+{
+  wchar_t *wc_key;
+  HKEY reg_key = NULL;
+  DWORD key_type;
+  DWORD nbytes;
+  char *value_utf8;
+
+  value_utf8 = NULL;
+  
+  nbytes = 0;
+  wc_key = g_utf8_to_utf16 (subdir, -1, NULL, NULL, NULL);
+  if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
+                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS &&
+      RegQueryValueExW (reg_key, key_name, 0,
+                       &key_type, NULL, &nbytes) == ERROR_SUCCESS &&
+      key_type == REG_SZ)
+    {
+      wchar_t *wc_temp = g_new (wchar_t, (nbytes+1)/2 + 1);
+      RegQueryValueExW (reg_key, key_name, 0,
+                       &key_type, (LPBYTE) wc_temp, &nbytes);
+      wc_temp[nbytes/2] = '\0';
+      value_utf8 = g_utf16_to_utf8 (wc_temp, -1, NULL, NULL, NULL);
+      g_free (wc_temp);
+    }
+  g_free (wc_key);
+  
+  if (reg_key != NULL)
+    RegCloseKey (reg_key);
+
+  return value_utf8;
+}
+
+/**
+ * g_content_type_equals:
+ * @type1: a content type string.
+ * @type2: a content type string.
+ *
+ * Compares two content types for equality.
+ *
+ * Returns: %TRUE if the two strings are identical or equivalent,
+ * %FALSE otherwise.
+ **/  
+gboolean
+g_content_type_equals (const char *type1,
+                      const char *type2)
+{
+  char *progid1, *progid2;
+  gboolean res;
+  
+  g_return_val_if_fail (type1 != NULL, FALSE);
+  g_return_val_if_fail (type2 != NULL, FALSE);
+
+  if (g_ascii_strcasecmp (type1, type2) == 0)
+    return TRUE;
+
+  res = FALSE;
+  progid1 = get_registry_classes_key (type1, NULL);
+  progid2 = get_registry_classes_key (type2, NULL);
+  if (progid1 != NULL && progid2 != NULL &&
+      strcmp (progid1, progid2) == 0)
+    res = TRUE;
+  g_free (progid1);
+  g_free (progid2);
+  
+  return res;
+}
+
+/**
+ * g_content_type_is_a:
+ * @type: a content type string. a content type string.
+ * @supertype: a string.
+ *
+ * Determines if @type is a subset of @supertype.  
+ *
+ * Returns: %TRUE if @type is a kind of @supertype,
+ * %FALSE otherwise. 
+ **/  
+gboolean
+g_content_type_is_a (const char   *type,
+                    const char   *supertype)
+{
+  gboolean res;
+  char *value_utf8;
+
+  g_return_val_if_fail (type != NULL, FALSE);
+  g_return_val_if_fail (supertype != NULL, FALSE);
+
+  if (g_content_type_equals (type, supertype))
+    return TRUE;
+
+  res = FALSE;
+  value_utf8 = get_registry_classes_key (type, L"PerceivedType");
+  if (value_utf8 && strcmp (value_utf8, supertype) == 0)
+    res = TRUE;
+  g_free (value_utf8);
+  
+  return res;
+}
+
+/**
+ * g_content_type_is_unknown:
+ * @type: a content type string. a content type string.
+ * 
+ * Returns:
+ **/  
+gboolean
+g_content_type_is_unknown (const char *type)
+{
+  g_return_val_if_fail (type != NULL, FALSE);
+
+  return strcmp ("*", type) == 0;
+}
+
+/**
+ * g_content_type_get_description:
+ * @type: a content type string. a content type string.
+ * 
+ * Returns: a short description of the content type @type. 
+ **/  
+char *
+g_content_type_get_description (const char *type)
+{
+  char *progid;
+  char *description;
+
+  g_return_val_if_fail (type != NULL, NULL);
+
+  progid = get_registry_classes_key (type, NULL);
+  if (progid)
+    {
+      description = get_registry_classes_key (progid, NULL);
+      g_free (progid);
+
+      if (description)
+       return description;
+    }
+
+  if (g_content_type_is_unknown (type))
+    return g_strdup (_("Unknown type"));
+  return g_strdup_printf (_("%s filetype"), type);
+}
+
+/**
+ * g_content_type_get_mime_type:
+ * @type: a content type string. a content type string.
+ * 
+ * Returns: the registered mime-type for the given @type.
+ **/  
+char *
+g_content_type_get_mime_type (const char   *type)
+{
+  char *mime;
+
+  g_return_val_if_fail (type != NULL, NULL);
+
+  mime = get_registry_classes_key (type, L"Content Type");
+  if (mime)
+    return mime;
+  else if (g_content_type_is_unknown (type))
+    return g_strdup ("application/octet-stream");
+  else if (*type == '.')
+    return g_strdup_printf ("application/x-ext-%s", type+1);
+  /* TODO: Map "image" to "image/ *", etc? */
+
+  return g_strdup ("application/octet-stream");
+}
+
+/**
+ * g_content_type_get_icon:
+ * @type: a content type string. a content type string.
+ * 
+ * Returns: #GIcon corresponding to the content type.
+ **/  
+GIcon *
+g_content_type_get_icon (const char   *type)
+{
+  g_return_val_if_fail (type != NULL, NULL);
+
+  /* TODO: How do we represent icons???
+     In the registry they are the default value of
+     HKEY_CLASSES_ROOT\<progid>\DefaultIcon with typical values like:
+     <type>: <value>
+     REG_EXPAND_SZ: %SystemRoot%\System32\Wscript.exe,3
+     REG_SZ: shimgvw.dll,3
+  */
+  return NULL;
+}
+
+/**
+ * g_content_type_can_be_executable:
+ * @type: a content type string.
+ * 
+ * Returns: %TRUE if the file type corresponds to something that
+ * can be executable, %FALSE otherwise. Note that for instance
+ * things like textfiles can be executables (i.e. scripts)
+ **/  
+gboolean
+g_content_type_can_be_executable (const char   *type)
+{
+  g_return_val_if_fail (type != NULL, FALSE);
+
+  if (strcmp (type, ".exe") == 0 ||
+      strcmp (type, ".com") == 0 ||
+      strcmp (type, ".bat") == 0)
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean
+looks_like_text (const guchar *data, gsize data_size)
+{
+  gsize i;
+  guchar c;
+  for (i = 0; i < data_size; i++)
+    {
+      c = data[i];
+      if (g_ascii_iscntrl (c) && !g_ascii_isspace (c))
+       return FALSE;
+    }
+  return TRUE;
+}
+
+/**
+ * g_content_type_guess:
+ * @filename: a string.
+ * @data: a stream of data.
+ * @data_size: the size of @data.
+ * @result_uncertain: a flag indicating the certainty of the 
+ * result.
+ * 
+ * Returns: a string indicating a guessed content type for the 
+ * given data. If the function is uncertain, @result_uncertain 
+ * will be set to %TRUE.
+ **/  
+char *
+g_content_type_guess (const char   *filename,
+                     const guchar *data,
+                     gsize         data_size,
+                     gboolean     *result_uncertain)
+{
+  char *basename;
+  char *type;
+  char *dot;
+
+  type = NULL;
+
+  if (filename)
+    {
+      basename = g_path_get_basename (filename);
+      dot = strrchr (basename, '.');
+      if (dot)
+       type = g_strdup (dot);
+      g_free (basename);
+    }
+
+  if (type)
+    return type;
+
+  if (data && looks_like_text (data, data_size))
+    return g_strdup (".txt");
+
+  return g_strdup ("*");
+}
+
+/**
+ * g_content_types_get_registered:
+ * 
+ * Returns: #GList of the registered content types.
+ **/  
+GList *
+g_content_types_get_registered (void)
+{
+  DWORD index;
+  wchar_t keyname[256];
+  DWORD key_len;
+  char *key_utf8;
+  GList *types;
+
+  types = NULL;
+  index = 0;
+  key_len = 256;
+  while (RegEnumKeyExW(HKEY_CLASSES_ROOT,
+                      index,
+                      keyname,
+                      &key_len,
+                      NULL,
+                      NULL,
+                      NULL,
+                      NULL) == ERROR_SUCCESS)
+    {
+      key_utf8 = g_utf16_to_utf8 (keyname, -1, NULL, NULL, NULL);
+      if (key_utf8)
+       {
+         if (*key_utf8 == '.')
+           types = g_list_prepend (types, key_utf8);
+         else
+           g_free (key_utf8);
+       }
+      index++;
+      key_len = 256;
+    }
+  
+  return g_list_reverse (types);
+}
+
+#else /* !G_OS_WIN32 - Unix specific version */
+
+#define XDG_PREFIX _gio_xdg
+#include "xdgmime/xdgmime.h"
+
+/* We lock this mutex whenever we modify global state in this module.  */
+G_LOCK_DEFINE_STATIC (gio_xdgmime);
+
+gsize
+_g_unix_content_type_get_sniff_len (void)
+{
+  gsize size;
+
+  G_LOCK (gio_xdgmime);
+  size = xdg_mime_get_max_buffer_extents ();
+  G_UNLOCK (gio_xdgmime);
+
+  return size;
+}
+
+char *
+_g_unix_content_type_unalias (const char *type)
+{
+  char *res;
+  
+  G_LOCK (gio_xdgmime);
+  res = g_strdup (xdg_mime_unalias_mime_type (type));
+  G_UNLOCK (gio_xdgmime);
+  
+  return res;
+}
+
+char **
+_g_unix_content_type_get_parents (const char *type)
+{
+  const char *umime;
+  const char **parents;
+  GPtrArray *array;
+  int i;
+
+  array = g_ptr_array_new ();
+  
+  G_LOCK (gio_xdgmime);
+  
+  umime = xdg_mime_unalias_mime_type (type);
+  g_ptr_array_add (array, g_strdup (umime));
+  
+  parents = xdg_mime_get_mime_parents (umime);
+  for (i = 0; parents && parents[i] != NULL; i++)
+    g_ptr_array_add (array, g_strdup (parents[i]));
+  
+  G_UNLOCK (gio_xdgmime);
+  
+  g_ptr_array_add (array, NULL);
+  
+  return (char **)g_ptr_array_free (array, FALSE);
+}
+
+gboolean
+g_content_type_equals (const char   *type1,
+                      const char   *type2)
+{
+  gboolean res;
+  
+  g_return_val_if_fail (type1 != NULL, FALSE);
+  g_return_val_if_fail (type2 != NULL, FALSE);
+  
+  G_LOCK (gio_xdgmime);
+  res = xdg_mime_mime_type_equal (type1, type2);
+  G_UNLOCK (gio_xdgmime);
+       
+  return res;
+}
+
+gboolean
+g_content_type_is_a (const char   *type,
+                    const char   *supertype)
+{
+  gboolean res;
+    
+  g_return_val_if_fail (type != NULL, FALSE);
+  g_return_val_if_fail (supertype != NULL, FALSE);
+  
+  G_LOCK (gio_xdgmime);
+  res = xdg_mime_mime_type_subclass (type, supertype);
+  G_UNLOCK (gio_xdgmime);
+       
+  return res;
+}
+
+gboolean
+g_content_type_is_unknown (const char *type)
+{
+  g_return_val_if_fail (type != NULL, FALSE);
+
+  return strcmp (XDG_MIME_TYPE_UNKNOWN, type) == 0;
+}
+
+
+typedef enum {
+  MIME_TAG_TYPE_OTHER,
+  MIME_TAG_TYPE_COMMENT
+} MimeTagType;
+
+typedef struct {
+  int current_type;
+  int current_lang_level;
+  int comment_lang_level;
+  char *comment;
+} MimeParser;
+
+
+static int
+language_level (const char *lang)
+{
+  const char * const *lang_list;
+  int i;
+  
+  /* The returned list is sorted from most desirable to least
+     desirable and always contains the default locale "C". */
+  lang_list = g_get_language_names ();
+  
+  for (i = 0; lang_list[i]; i++)
+    if (strcmp (lang_list[i], lang) == 0)
+      return 1000-i;
+  
+  return 0;
+}
+
+static void
+mime_info_start_element (GMarkupParseContext *context,
+                        const gchar         *element_name,
+                        const gchar        **attribute_names,
+                        const gchar        **attribute_values,
+                        gpointer             user_data,
+                        GError             **error)
+{
+  int i;
+  const char *lang;
+  MimeParser *parser = user_data;
+  
+  if (strcmp (element_name, "comment") == 0)
+    {
+      lang = "C";
+      for (i = 0; attribute_names[i]; i++)
+       if (strcmp (attribute_names[i], "xml:lang") == 0)
+         {
+           lang = attribute_values[i];
+           break;
+         }
+      
+      parser->current_lang_level = language_level (lang);
+      parser->current_type = MIME_TAG_TYPE_COMMENT;
+    }
+  else
+    parser->current_type = MIME_TAG_TYPE_OTHER;
+  
+}
+
+static void
+mime_info_end_element (GMarkupParseContext *context,
+                      const gchar         *element_name,
+                      gpointer             user_data,
+                      GError             **error)
+{
+  MimeParser *parser = user_data;
+  
+  parser->current_type = MIME_TAG_TYPE_OTHER;
+}
+
+static void
+mime_info_text (GMarkupParseContext *context,
+               const gchar         *text,
+               gsize                text_len,  
+               gpointer             user_data,
+               GError             **error)
+{
+  MimeParser *parser = user_data;
+
+  if (parser->current_type == MIME_TAG_TYPE_COMMENT &&
+      parser->current_lang_level > parser->comment_lang_level)
+    {
+      g_free (parser->comment);
+      parser->comment = g_strndup (text, text_len);
+      parser->comment_lang_level = parser->current_lang_level;
+    }
+}
+
+static char *
+load_comment_for_mime_helper (const char *dir, const char *basename)
+{
+  GMarkupParseContext *context;
+  char *filename, *data;
+  gsize len;
+  gboolean res;
+  MimeParser parse_data = {0};
+  GMarkupParser parser = {
+    mime_info_start_element,
+    mime_info_end_element,
+    mime_info_text
+  };
+
+  filename = g_build_filename (dir, "mime", basename, NULL);
+  
+  res = g_file_get_contents (filename,  &data,  &len,  NULL);
+  g_free (filename);
+  if (!res)
+    return NULL;
+  
+  context = g_markup_parse_context_new   (&parser, 0, &parse_data, NULL);
+  res = g_markup_parse_context_parse (context, data, len, NULL);
+  g_free (data);
+  g_markup_parse_context_free (context);
+  
+  if (!res)
+    return NULL;
+
+  return parse_data.comment;
+}
+
+
+static char *
+load_comment_for_mime (const char *mimetype)
+{
+  const char * const* dirs;
+  char *basename;
+  char *comment;
+  int i;
+
+  basename = g_strdup_printf ("%s.xml", mimetype);
+
+  comment = load_comment_for_mime_helper (g_get_user_data_dir (), basename);
+  if (comment)
+    {
+      g_free (basename);
+      return comment;
+    }
+  
+  dirs = g_get_system_data_dirs ();
+
+  for (i = 0; dirs[i] != NULL; i++)
+    {
+      comment = load_comment_for_mime_helper (dirs[i], basename);
+      if (comment)
+       {
+         g_free (basename);
+         return comment;
+       }
+    }
+  g_free (basename);
+  
+  return g_strdup_printf (_("%s type"), mimetype);
+}
+
+char *
+g_content_type_get_description (const char *type)
+{
+  static GHashTable *type_comment_cache = NULL;
+  char *comment;
+
+  g_return_val_if_fail (type != NULL, NULL);
+  
+  G_LOCK (gio_xdgmime);
+  if (type_comment_cache == NULL)
+    type_comment_cache = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+  comment = g_hash_table_lookup (type_comment_cache, type);
+  comment = g_strdup (comment);
+  G_UNLOCK (gio_xdgmime);
+  
+  if (comment != NULL)
+    return comment;
+
+  comment = load_comment_for_mime (type);
+  
+  G_LOCK (gio_xdgmime);
+  g_hash_table_insert (type_comment_cache,
+                      g_strdup (type),
+                      g_strdup (comment));
+  G_UNLOCK (gio_xdgmime);
+
+  return comment;
+}
+
+char *
+g_content_type_get_mime_type (const char   *type)
+{
+  g_return_val_if_fail (type != NULL, NULL);
+
+  return g_strdup (type);
+}
+
+GIcon *
+g_content_type_get_icon (const char   *type)
+{
+  g_return_val_if_fail (type != NULL, NULL);
+
+  /* TODO: Implement */
+  return NULL;
+}
+
+/**
+ * g_content_type_can_be_executable:
+ * @type: a content type string.
+ * 
+ * Returns: %TRUE if the file type corresponds to something that
+ * can be executable, %FALSE otherwise. Note that for instance
+ * things like textfiles can be executables (i.e. scripts)
+ **/  
+gboolean
+g_content_type_can_be_executable (const char   *type)
+{
+  g_return_val_if_fail (type != NULL, FALSE);
+
+  if (g_content_type_is_a (type, "application/x-executable")  ||
+      g_content_type_is_a (type, "text/plain"))
+    return TRUE;
+
+  return FALSE;
+}
+
+static gboolean
+looks_like_text (const guchar *data, gsize data_size)
+{
+  gsize i;
+  for (i = 0; i < data_size; i++)
+    {
+      if g_ascii_iscntrl (data[i])
+       return FALSE;
+    }
+  return TRUE;
+}
+
+char *
+g_content_type_guess (const char   *filename,
+                     const guchar *data,
+                     gsize         data_size,
+                     gboolean     *result_uncertain)
+{
+  char *basename;
+  const char *name_mimetypes[10], *sniffed_mimetype;
+  char *mimetype;
+  int i;
+  int n_name_mimetypes;
+  int sniffed_prio;
+
+  sniffed_prio = 0;
+  n_name_mimetypes = 0;
+  sniffed_mimetype = XDG_MIME_TYPE_UNKNOWN;
+
+  if (result_uncertain)
+    *result_uncertain = FALSE;
+  
+  G_LOCK (gio_xdgmime);
+  
+  if (filename)
+    {
+      basename = g_path_get_basename (filename);
+      n_name_mimetypes = xdg_mime_get_mime_types_from_file_name (basename, name_mimetypes, 10);
+      g_free (basename);
+    }
+
+  /* Got an extension match, and no conflicts. This is it. */
+  if (n_name_mimetypes == 1)
+    {
+      G_UNLOCK (gio_xdgmime);
+      return g_strdup (name_mimetypes[0]);
+    }
+  
+  if (data)
+    {
+      sniffed_mimetype = xdg_mime_get_mime_type_for_data (data, data_size, &sniffed_prio);
+      if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
+         data &&
+         looks_like_text (data, data_size))
+       sniffed_mimetype = "text/plain";
+    }
+  
+  if (n_name_mimetypes == 0)
+    {
+      if (sniffed_mimetype == XDG_MIME_TYPE_UNKNOWN &&
+         result_uncertain)
+       *result_uncertain = TRUE;
+      
+      mimetype = g_strdup (sniffed_mimetype);
+    }
+  else
+    {
+      mimetype = NULL;
+      if (sniffed_mimetype != XDG_MIME_TYPE_UNKNOWN)
+       {
+         if (sniffed_prio >= 80) /* High priority sniffing match, use that */
+           mimetype = g_strdup (sniffed_mimetype);
+         else
+           {
+             /* There are conflicts between the name matches and we have a sniffed
+                type, use that as a tie breaker. */
+             
+             for (i = 0; i < n_name_mimetypes; i++)
+               {
+                 if ( xdg_mime_mime_type_subclass (name_mimetypes[i], sniffed_mimetype))
+                   {
+                     /* This nametype match is derived from (or the same as) the sniffed type).
+                        This is probably it. */
+                     mimetype = g_strdup (name_mimetypes[i]);
+                     break;
+                   }
+               }
+           }
+       }
+      
+      if (mimetype == NULL)
+       {
+         /* Conflicts, and sniffed type was no help or not there. guess on the first one */
+         mimetype = g_strdup (name_mimetypes[0]);
+         if (result_uncertain)
+           *result_uncertain = TRUE;
+       }
+    }
+  
+  G_UNLOCK (gio_xdgmime);
+
+  return mimetype;
+}
+
+static gboolean
+foreach_mimetype (gpointer  key,
+                 gpointer  value,
+                 gpointer  user_data)
+{
+  GList **l = user_data;
+
+  *l = g_list_prepend (*l, (char *)key);
+  return TRUE;
+}
+
+static void
+enumerate_mimetypes_subdir (const char *dir, const char *prefix, GHashTable *mimetypes)
+{
+  DIR *d;
+  struct dirent *ent;
+  char *mimetype;
+
+  d = opendir (dir);
+  if (d)
+    {
+      while ((ent = readdir (d)) != NULL)
+       {
+         if (g_str_has_suffix (ent->d_name, ".xml"))
+           {
+             mimetype = g_strdup_printf ("%s/%.*s", prefix, (int) strlen (ent->d_name) - 4, ent->d_name);
+             g_hash_table_insert (mimetypes, mimetype, NULL);
+           }
+       }
+      closedir (d);
+    }
+}
+
+static void
+enumerate_mimetypes_dir (const char *dir, GHashTable *mimetypes)
+{
+  DIR *d;
+  struct dirent *ent;
+  char *mimedir;
+  char *name;
+
+  mimedir = g_build_filename (dir, "mime", NULL);
+  
+  d = opendir (mimedir);
+  if (d)
+    {
+      while ((ent = readdir (d)) != NULL)
+       {
+         if (strcmp (ent->d_name, "packages") != 0)
+           {
+             name = g_build_filename (mimedir, ent->d_name, NULL);
+             if (g_file_test (name, G_FILE_TEST_IS_DIR))
+               enumerate_mimetypes_subdir (name, ent->d_name, mimetypes);
+             g_free (name);
+           }
+       }
+      closedir (d);
+    }
+  
+  g_free (mimedir);
+}
+
+GList *
+g_content_types_get_registered (void)
+{
+  const char * const* dirs;
+  GHashTable *mimetypes;
+  int i;
+  GList *l;
+
+  mimetypes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+  enumerate_mimetypes_dir (g_get_user_data_dir (), mimetypes);
+  dirs = g_get_system_data_dirs ();
+
+  for (i = 0; dirs[i] != NULL; i++)
+    enumerate_mimetypes_dir (dirs[i], mimetypes);
+
+  l = NULL;
+  g_hash_table_foreach_steal (mimetypes, foreach_mimetype, &l);
+  g_hash_table_destroy (mimetypes);
+
+  return l;
+}
+
+#endif /* Unix version */
diff --git a/gio/gcontenttype.h b/gio/gcontenttype.h
new file mode 100644 (file)
index 0000000..e8bbf57
--- /dev/null
@@ -0,0 +1,50 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_CONTENT_TYPE_H__
+#define __G_CONTENT_TYPE_H__
+
+#include <glib.h>
+#include <gio/gicon.h>
+
+G_BEGIN_DECLS
+
+gboolean g_content_type_equals            (const char   *type1,
+                                          const char   *type2);
+gboolean g_content_type_is_a              (const char   *type,
+                                          const char   *supertype);
+gboolean g_content_type_is_unknown        (const char   *type);
+char *   g_content_type_get_description   (const char   *type);
+char *   g_content_type_get_mime_type     (const char   *type);
+GIcon *  g_content_type_get_icon          (const char   *type);
+gboolean g_content_type_can_be_executable (const char   *type);
+
+char *   g_content_type_guess             (const char   *filename,
+                                          const guchar *data,
+                                          gsize         data_size,
+                                          gboolean     *result_uncertain );
+
+GList *  g_content_types_get_registered   (void);
+
+G_END_DECLS
+
+#endif /* __G_CONTENT_TYPE_H__ */
diff --git a/gio/gcontenttypeprivate.h b/gio/gcontenttypeprivate.h
new file mode 100644 (file)
index 0000000..a94d138
--- /dev/null
@@ -0,0 +1,36 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_CONTENT_TYPE_PRIVATE_H__
+#define __G_CONTENT_TYPE_PRIVATE_H__
+
+#include "gcontenttype.h"
+
+G_BEGIN_DECLS
+
+gsize  _g_unix_content_type_get_sniff_len (void);
+char * _g_unix_content_type_unalias       (const char *type);
+char **_g_unix_content_type_get_parents   (const char *type);
+
+G_END_DECLS
+
+#endif /* __G_CONTENT_TYPE_PRIVATE_H__ */
diff --git a/gio/gdatainputstream.c b/gio/gdatainputstream.c
new file mode 100644 (file)
index 0000000..54a4678
--- /dev/null
@@ -0,0 +1,718 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gdatainputstream.h"
+#include "glibintl.h"
+
+struct _GDataInputStreamPrivate {
+  GDataStreamByteOrder byte_order;
+  GDataStreamNewlineType newline_type;
+};
+
+enum {
+  PROP_0
+};
+
+static void g_data_input_stream_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec);
+static void g_data_input_stream_get_property (GObject      *object,
+                                             guint         prop_id,
+                                             GValue       *value,
+                                             GParamSpec   *pspec);
+
+G_DEFINE_TYPE (GDataInputStream,
+               g_data_input_stream,
+               G_TYPE_BUFFERED_INPUT_STREAM)
+
+
+static void
+g_data_input_stream_class_init (GDataInputStreamClass *klass)
+{
+  GObjectClass *object_class;
+
+  g_type_class_add_private (klass, sizeof (GDataInputStreamPrivate));
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = g_data_input_stream_get_property;
+  object_class->set_property = g_data_input_stream_set_property;
+}
+
+static void
+g_data_input_stream_set_property (GObject         *object,
+                                 guint            prop_id,
+                                 const GValue    *value,
+                                 GParamSpec      *pspec)
+{
+  GDataInputStreamPrivate *priv;
+  GDataInputStream        *dstream;
+
+  dstream = G_DATA_INPUT_STREAM (object);
+  priv = dstream->priv;
+
+  switch (prop_id) 
+    {
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_data_input_stream_get_property (GObject    *object,
+                                      guint       prop_id,
+                                      GValue     *value,
+                                      GParamSpec *pspec)
+{
+  GDataInputStreamPrivate *priv;
+  GDataInputStream        *dstream;
+
+  dstream = G_DATA_INPUT_STREAM (object);
+  priv = dstream->priv;
+
+  switch (prop_id)
+    { 
+  
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+static void
+g_data_input_stream_init (GDataInputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                              G_TYPE_DATA_INPUT_STREAM,
+                                              GDataInputStreamPrivate);
+
+  stream->priv->byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
+  stream->priv->newline_type = G_DATA_STREAM_NEWLINE_TYPE_LF;
+}
+
+/**
+ * g_data_input_stream_new:
+ * @base_stream: a given #GInputStream.
+ * 
+ * Returns: a new #GDataInputStream.
+ **/
+GDataInputStream *
+g_data_input_stream_new (GInputStream *base_stream)
+{
+  GDataInputStream *stream;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (base_stream), NULL);
+
+  stream = g_object_new (G_TYPE_DATA_INPUT_STREAM,
+                         "base-stream", base_stream,
+                         NULL);
+
+  return stream;
+}
+
+/**
+ * g_data_input_stream_set_byte_order:
+ * @stream: a given #GDataInputStream.
+ * @order: a #GDataStreamByteOrder to set.
+ * 
+ * This function sets the byte order for the given @stream. All subsequent
+ * reads from the @stream will be read in the given @order.
+ *  
+ **/
+void
+g_data_input_stream_set_byte_order (GDataInputStream *stream,
+                                   GDataStreamByteOrder order)
+{
+  g_return_if_fail (G_IS_DATA_INPUT_STREAM (stream));
+
+  stream->priv->byte_order = order;
+}
+
+/**
+ * g_data_input_stream_get_byte_order:
+ * @stream: a given #GDataInputStream.
+ * 
+ * Returns the @stream's current #GDataStreamByteOrder. 
+ **/
+GDataStreamByteOrder
+g_data_input_stream_get_byte_order (GDataInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
+
+  return stream->priv->byte_order;
+}
+
+/**
+ * g_data_input_stream_set_newline_type:
+ * @stream: a given #GDataInputStream.
+ * @type: the type of new line return as #GDataStreamNewlineType.
+ * 
+ * Sets the newline type for the @stream.
+ * 
+ * TODO: is it valid to set this to G_DATA_STREAM_NEWLINE_TYPE_ANY, or
+ * should it always be set to {_LF, _CR, _CR_LF}
+ *  
+ **/
+void
+g_data_input_stream_set_newline_type (GDataInputStream        *stream,
+                                     GDataStreamNewlineType   type)
+{
+  g_return_if_fail (G_IS_DATA_INPUT_STREAM (stream));
+
+  stream->priv->newline_type = type;
+}
+
+/**
+ * g_data_input_stream_get_newline_type:
+ * @stream: a given #GDataInputStream.
+ * 
+ * Gets the current newline type for the @stream.
+ * 
+ * Returns: #GDataStreamNewlineType for the given @stream.
+ **/
+GDataStreamNewlineType
+g_data_input_stream_get_newline_type (GDataInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), G_DATA_STREAM_NEWLINE_TYPE_ANY);
+
+  return stream->priv->newline_type;
+}
+
+static gboolean
+read_data (GDataInputStream *stream,
+         void *buffer,
+         gsize size,
+         GCancellable *cancellable,
+         GError **error)
+{
+  gsize available;
+  gssize res;
+
+  while ((available = g_buffered_input_stream_get_available (G_BUFFERED_INPUT_STREAM (stream))) < size)
+    {
+      res = g_buffered_input_stream_fill (G_BUFFERED_INPUT_STREAM (stream),
+                                         size - available,
+                                         cancellable, error);
+      if (res < 0)
+       return FALSE;
+      if (res == 0)
+       {
+         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                      _("Unexpected early end-of-stream"));
+         return FALSE;
+       }
+    }
+  
+  /* This should always succeed, since its in the buffer */
+  res = g_input_stream_read (G_INPUT_STREAM (stream),
+                            buffer, size,
+                            NULL, NULL);
+  g_assert (res == size);
+  return TRUE;
+}
+
+
+/**
+ * g_data_input_stream_read_byte:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * 
+ * Returns: an unsigned 8-bit/1-byte value read from the @stream or %0 
+ * if an error occured.
+ **/
+
+guchar
+g_data_input_stream_read_byte (GDataInputStream        *stream,
+                             GCancellable            *cancellable,
+                             GError                 **error)
+{
+  guchar c;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), '\0');
+  
+  if (read_data (stream, &c, 1, cancellable, error))
+      return c;
+  
+  return 0;
+}
+
+
+/**
+ * g_data_input_stream_read_int16:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * 
+ * Returns a signed 16-bit/2-byte value read from @stream or %0 if 
+ * an error occured.
+ **/
+gint16
+g_data_input_stream_read_int16 (GDataInputStream        *stream,
+                              GCancellable            *cancellable,
+                              GError                 **error)
+{
+  gint16 v;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), 0);
+  
+  if (read_data (stream, &v, 2, cancellable, error))
+    {
+      switch (stream->priv->byte_order)
+       {
+       case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+         v = GINT16_FROM_BE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+         v = GINT16_FROM_LE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+       default:
+         break;
+       }
+      return v;
+    }
+  
+  return 0;
+}
+
+
+/**
+ * g_data_input_stream_read_uint16:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ *
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order(). 
+ * 
+ * Returns an unsigned 16-bit/2-byte value read from the @stream or %0 if 
+ * an error occured. 
+ **/
+guint16
+g_data_input_stream_read_uint16 (GDataInputStream        *stream,
+                               GCancellable            *cancellable,
+                               GError                 **error)
+{
+  guint16 v;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), 0);
+  
+  if (read_data (stream, &v, 2, cancellable, error))
+    {
+      switch (stream->priv->byte_order)
+       {
+       case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+         v = GUINT16_FROM_BE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+         v = GUINT16_FROM_LE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+       default:
+         break;
+       }
+      return v;
+    }
+  
+  return 0;
+}
+
+
+/**
+ * g_data_input_stream_read_int32:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ *   
+ * Returns a signed 32-bit/4-byte value read from the @stream or %0 if 
+ * an error occured. 
+ **/
+gint32
+g_data_input_stream_read_int32 (GDataInputStream        *stream,
+                              GCancellable            *cancellable,
+                              GError                 **error)
+{
+  gint32 v;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), 0);
+  
+  if (read_data (stream, &v, 4, cancellable, error))
+    {
+      switch (stream->priv->byte_order)
+       {
+       case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+         v = GINT32_FROM_BE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+         v = GINT32_FROM_LE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+       default:
+         break;
+       }
+      return v;
+    }
+  
+  return 0;
+}
+
+
+/**
+ * g_data_input_stream_read_uint32:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * 
+ * Returns an unsigned 32-bit/4-byte value read from the @stream or %0 if 
+ * an error occured. 
+ **/
+guint32
+g_data_input_stream_read_uint32 (GDataInputStream        *stream,
+                               GCancellable            *cancellable,
+                               GError                 **error)
+{
+  guint32 v;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), 0);
+  
+  if (read_data (stream, &v, 4, cancellable, error))
+    {
+      switch (stream->priv->byte_order)
+       {
+       case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+         v = GUINT32_FROM_BE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+         v = GUINT32_FROM_LE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+       default:
+         break;
+       }
+      return v;
+    }
+  
+  return 0;
+}
+
+
+/**
+ * g_data_input_stream_read_int64:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order() and g_data_stream_set_byte_order().
+ * 
+ * Returns a signed 64-bit/8-byte value read from @stream or %0 if 
+ * an error occured.  
+ **/
+gint64
+g_data_input_stream_read_int64 (GDataInputStream        *stream,
+                              GCancellable            *cancellable,
+                              GError                 **error)
+{
+  gint64 v;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), 0);
+  
+  if (read_data (stream, &v, 8, cancellable, error))
+    {
+      switch (stream->priv->byte_order)
+       {
+       case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+         v = GINT64_FROM_BE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+         v = GINT64_FROM_LE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+       default:
+         break;
+       }
+      return v;
+    }
+  
+  return 0;
+}
+
+
+/**
+ * g_data_input_stream_read_uint64:
+ * @stream: a given #GDataInputStream.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * In order to get the correct byte order for this read operation, 
+ * see g_data_stream_get_byte_order().
+ * 
+ * Returns an unsigned 64-bit/8-byte read from @stream or %0 if 
+ * an error occured. 
+ **/
+guint64
+g_data_input_stream_read_uint64 (GDataInputStream        *stream,
+                               GCancellable            *cancellable,
+                               GError                 **error)
+{
+  guint64 v;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), 0);
+  
+  if (read_data (stream, &v, 8, cancellable, error))
+    {
+      switch (stream->priv->byte_order)
+       {
+       case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+         v = GUINT64_FROM_BE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+         v = GUINT64_FROM_LE (v);
+         break;
+       case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+       default:
+         break;
+       }
+      return v;
+    }
+  
+  return 0;
+}
+
+static gssize
+scan_for_newline (GDataInputStream *stream,
+                 gsize *checked_out,
+                 gboolean *last_saw_cr_out,
+                 int *newline_len_out)
+{
+  GBufferedInputStream *bstream;
+  GDataInputStreamPrivate *priv;
+  char buffer[100];
+  gsize start, end, peeked;
+  int i;
+  gssize found_pos;
+  int newline_len;
+  gsize available, checked;
+  gboolean last_saw_cr;
+
+  priv = stream->priv;
+  
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+  
+  available = g_buffered_input_stream_get_available (bstream);
+
+  checked = *checked_out;
+  last_saw_cr = *last_saw_cr_out;
+  found_pos = -1;
+  newline_len = 0;
+  
+  while (checked < available)
+    {
+      start = checked;
+      end = MIN (start + sizeof(buffer), available);
+      peeked = g_buffered_input_stream_peek (bstream, buffer, start, end - start);
+      end = start + peeked;
+
+      for (i = 0; i < peeked; i++)
+       {
+         switch (priv->newline_type)
+           {
+           case G_DATA_STREAM_NEWLINE_TYPE_LF:
+             if (buffer[i] == 10)
+               {
+                 found_pos = start + i;
+                 newline_len = 1;
+               }
+             break;
+           case G_DATA_STREAM_NEWLINE_TYPE_CR:
+             if (buffer[i] == 13)
+               {
+                 found_pos = start + i;
+                 newline_len = 1;
+               }
+             break;
+           case G_DATA_STREAM_NEWLINE_TYPE_CR_LF:
+             if (last_saw_cr && buffer[i] == 10)
+               {
+                 found_pos = start + i - 1;
+                 newline_len = 2;
+               }
+             break;
+           default:
+           case G_DATA_STREAM_NEWLINE_TYPE_ANY:
+             if (buffer[i] == 10) /* LF */
+               {
+                 if (last_saw_cr)
+                   {
+                     /* CR LF */
+                     found_pos = start + i - 1;
+                     newline_len = 2;
+                   }
+                 else
+                   {
+                     /* LF */
+                     found_pos = start + i;
+                     newline_len = 1;
+                   }
+               }
+             else if (last_saw_cr)
+               {
+                 /* Last was cr, this is not LF, end is CR */
+                 found_pos = start + i - 1;
+                 newline_len = 1;
+               }
+             /* Don't check for CR here, instead look at last_saw_cr on next byte */
+             break;
+           }
+         
+         last_saw_cr = (buffer[i] == 13);
+
+         if (found_pos != -1)
+           {
+             *newline_len_out = newline_len;
+             return found_pos;
+           }
+       }
+      checked = end;
+    }
+
+  *checked_out = checked;
+  *last_saw_cr_out = last_saw_cr;
+  return -1;
+}
+                 
+
+/**
+ * g_data_input_stream_read_line:
+ * @stream: a given #GDataInputStream.
+ * @length: a #gsize to get the length of the data read in.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * Returns a string with the line that was read in. Set @length to 
+ * a #gsize to get the length of the read line. This function will 
+ * return %NULL on an error.
+ **/
+char *
+g_data_input_stream_read_line (GDataInputStream        *stream,
+                             gsize                   *length,
+                             GCancellable            *cancellable,
+                             GError                 **error)
+{
+  GBufferedInputStream *bstream;
+  gsize checked;
+  gboolean last_saw_cr;
+  gssize found_pos;
+  gssize res;
+  int newline_len;
+  char *line;
+  
+  g_return_val_if_fail (G_IS_DATA_INPUT_STREAM (stream), NULL);  
+
+  bstream = G_BUFFERED_INPUT_STREAM (stream);
+
+  newline_len = 0;
+  checked = 0;
+  last_saw_cr = FALSE;
+
+  while ((found_pos = scan_for_newline (stream, &checked, &last_saw_cr, &newline_len)) == -1)
+    {
+      if (g_buffered_input_stream_get_available (bstream) ==
+         g_buffered_input_stream_get_buffer_size (bstream))
+       g_buffered_input_stream_set_buffer_size (bstream,
+                                                2 * g_buffered_input_stream_get_buffer_size (bstream));
+
+      res = g_buffered_input_stream_fill (bstream, -1, cancellable, error);
+      if (res < 0)
+       return NULL;
+      if (res == 0)
+       {
+         /* End of stream */
+         if (g_buffered_input_stream_get_available (bstream) == 0)
+           {
+             if (length)
+               *length = 0;
+             return NULL;
+           }
+         else
+           {
+             found_pos = checked;
+             newline_len = 0;
+             break;
+           }
+       }
+    }
+
+  line = g_malloc (found_pos + newline_len + 1);
+
+  res = g_input_stream_read (G_INPUT_STREAM (stream),
+                            line,
+                            found_pos + newline_len,
+                            NULL, NULL);
+  if (length)
+    *length = (gsize)found_pos;
+  g_assert (res == found_pos + newline_len);
+  line[found_pos] = 0;
+  
+  return line;
+}
+
+
+/**
+ * g_data_input_stream_read_until:
+ * @stream: a given #GDataInputStream.
+ * @stop_char: character to terminate the read.
+ * @length: a #gsize to get the length of the data read in.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * NOTE: not supported for #GDataInputStream.
+ * Returns %NULL.
+ **/
+char *
+g_data_input_stream_read_until (GDataInputStream        *stream,
+                              gchar                    stop_char,
+                              gsize                   *length,
+                              GCancellable            *cancellable,
+                              GError                 **error)
+{
+  /* TODO: should be implemented */
+  g_assert_not_reached ();
+  return NULL;
+}
diff --git a/gio/gdatainputstream.h b/gio/gdatainputstream.h
new file mode 100644 (file)
index 0000000..af58dbf
--- /dev/null
@@ -0,0 +1,117 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DATA_INPUT_STREAM_H__
+#define __G_DATA_INPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gbufferedinputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DATA_INPUT_STREAM         (g_data_input_stream_get_type ())
+#define G_DATA_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DATA_INPUT_STREAM, GDataInputStream))
+#define G_DATA_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DATA_INPUT_STREAM, GDataInputStreamClass))
+#define G_IS_DATA_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DATA_INPUT_STREAM))
+#define G_IS_DATA_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DATA_INPUT_STREAM))
+#define G_DATA_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DATA_INPUT_STREAM, GDataInputStreamClass))
+
+typedef struct _GDataInputStream         GDataInputStream;
+typedef struct _GDataInputStreamClass    GDataInputStreamClass;
+typedef struct _GDataInputStreamPrivate  GDataInputStreamPrivate;
+
+struct _GDataInputStream
+{
+  GBufferedInputStream parent;
+
+  /*< private >*/
+  GDataInputStreamPrivate *priv;
+};
+
+struct _GDataInputStreamClass
+{
+ GBufferedInputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+typedef enum  {
+  G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN,
+  G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN,
+  G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN
+} GDataStreamByteOrder;
+
+typedef enum  {
+  G_DATA_STREAM_NEWLINE_TYPE_LF,
+  G_DATA_STREAM_NEWLINE_TYPE_CR,
+  G_DATA_STREAM_NEWLINE_TYPE_CR_LF,
+  G_DATA_STREAM_NEWLINE_TYPE_ANY
+} GDataStreamNewlineType;
+
+GType          g_data_input_stream_get_type   (void) G_GNUC_CONST;
+GDataInputStream*  g_data_input_stream_new        (GInputStream *base_stream);
+
+void                   g_data_input_stream_set_byte_order   (GDataInputStream        *stream,
+                                                            GDataStreamByteOrder     order);
+GDataStreamByteOrder   g_data_input_stream_get_byte_order   (GDataInputStream        *stream);
+void                   g_data_input_stream_set_newline_type (GDataInputStream        *data_stream,
+                                                            GDataStreamNewlineType   type);
+GDataStreamNewlineType g_data_input_stream_get_newline_type (GDataInputStream        *stream);
+guchar                 g_data_input_stream_read_byte        (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+gint16                 g_data_input_stream_read_int16       (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+guint16                g_data_input_stream_read_uint16      (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+gint32                 g_data_input_stream_read_int32       (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+guint32                g_data_input_stream_read_uint32      (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+gint64                 g_data_input_stream_read_int64       (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+guint64                g_data_input_stream_read_uint64      (GDataInputStream        *stream,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+char *                 g_data_input_stream_read_line        (GDataInputStream        *stream,
+                                                            gsize                   *length,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+char *                 g_data_input_stream_read_until       (GDataInputStream        *stream,
+                                                            gchar                    stop_char,
+                                                            gsize                   *length,
+                                                            GCancellable            *cancellable,
+                                                            GError                 **error);
+
+G_END_DECLS
+
+#endif /* __G_DATA_INPUT_STREAM_H__ */
diff --git a/gio/gdataoutputstream.c b/gio/gdataoutputstream.c
new file mode 100644 (file)
index 0000000..6393d7c
--- /dev/null
@@ -0,0 +1,441 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <string.h>
+#include "gdataoutputstream.h"
+#include "glibintl.h"
+
+struct _GDataOutputStreamPrivate {
+  GDataStreamByteOrder byte_order;
+};
+
+enum {
+  PROP_0
+};
+
+static void g_data_output_stream_set_property (GObject      *object,
+                                              guint         prop_id,
+                                              const GValue *value,
+                                              GParamSpec   *pspec);
+static void g_data_output_stream_get_property (GObject      *object,
+                                              guint         prop_id,
+                                              GValue       *value,
+                                              GParamSpec   *pspec);
+
+G_DEFINE_TYPE (GDataOutputStream,
+               g_data_output_stream,
+               G_TYPE_FILTER_OUTPUT_STREAM)
+
+
+static void
+g_data_output_stream_class_init (GDataOutputStreamClass *klass)
+{
+  GObjectClass *object_class;
+
+  g_type_class_add_private (klass, sizeof (GDataOutputStreamPrivate));
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = g_data_output_stream_get_property;
+  object_class->set_property = g_data_output_stream_set_property;
+}
+
+static void
+g_data_output_stream_set_property (GObject         *object,
+                                 guint            prop_id,
+                                 const GValue    *value,
+                                 GParamSpec      *pspec)
+{
+  GDataOutputStreamPrivate *priv;
+  GDataOutputStream        *dstream;
+
+  dstream = G_DATA_OUTPUT_STREAM (object);
+  priv = dstream->priv;
+
+  switch (prop_id) 
+    {
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_data_output_stream_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GDataOutputStreamPrivate *priv;
+  GDataOutputStream        *dstream;
+
+  dstream = G_DATA_OUTPUT_STREAM (object);
+  priv = dstream->priv;
+
+  switch (prop_id)
+    { 
+  
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+static void
+g_data_output_stream_init (GDataOutputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                              G_TYPE_DATA_OUTPUT_STREAM,
+                                              GDataOutputStreamPrivate);
+
+  stream->priv->byte_order = G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN;
+}
+
+/**
+ * g_data_output_stream_new:
+ * @base_stream: a #GOutputStream.
+ * 
+ * Returns: #GDataOutputStream.
+ **/
+GDataOutputStream *
+g_data_output_stream_new (GOutputStream *base_stream)
+{
+  GDataOutputStream *stream;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (base_stream), NULL);
+
+  stream = g_object_new (G_TYPE_DATA_OUTPUT_STREAM,
+                         "base-stream", base_stream,
+                         NULL);
+
+  return stream;
+}
+
+/**
+ * g_data_output_stream_set_byte_order:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @order: a %GDataStreamByteOrder.
+ * 
+ **/
+void
+g_data_output_stream_set_byte_order (GDataOutputStream *stream,
+                                   GDataStreamByteOrder order)
+{
+  g_return_if_fail (G_IS_DATA_OUTPUT_STREAM (stream));
+
+  stream->priv->byte_order = order;
+}
+
+/**
+ * g_data_output_stream_get_byte_order:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * 
+ * Returns: the %GDataStreamByteOrder for the @stream.
+ **/
+GDataStreamByteOrder
+g_data_output_stream_get_byte_order (GDataOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN);
+
+  return stream->priv->byte_order;
+}
+
+/**
+ * g_data_output_stream_put_byte:
+ * @data_stream: a #GDataOutputStream.
+ * @data: a #guchar.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_byte (GDataOutputStream     *data_stream,
+                              guchar                 data,
+                              GCancellable          *cancellable,
+                              GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (data_stream), FALSE);
+
+  return g_output_stream_write_all (G_OUTPUT_STREAM (data_stream),
+                                   &data, 1,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_int16:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @data: a #gint16.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_int16 (GDataOutputStream     *stream,
+                               gint16                 data,
+                               GCancellable          *cancellable,
+                               GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+
+  switch (stream->priv->byte_order)
+    {
+    case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+      data = GINT16_TO_BE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+      data = GINT16_TO_LE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+    default:
+      break;
+    }
+  
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   &data, 2,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_uint16:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @data: a #guint16.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_uint16 (GDataOutputStream     *stream,
+                                guint16                data,
+                                GCancellable          *cancellable,
+                                GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+
+  switch (stream->priv->byte_order)
+    {
+    case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+      data = GUINT16_TO_BE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+      data = GUINT16_TO_LE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+    default:
+      break;
+    }
+  
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   &data, 2,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_int32:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @data: a #gint32.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_int32 (GDataOutputStream     *stream,
+                               gint32                 data,
+                               GCancellable          *cancellable,
+                               GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+
+  switch (stream->priv->byte_order)
+    {
+    case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+      data = GINT32_TO_BE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+      data = GINT32_TO_LE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+    default:
+      break;
+    }
+  
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   &data, 4,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_uint32:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @data: a #guint32.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_uint32 (GDataOutputStream     *stream,
+                                guint32                data,
+                                GCancellable          *cancellable,
+                                GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+
+  switch (stream->priv->byte_order)
+    {
+    case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+      data = GUINT32_TO_BE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+      data = GUINT32_TO_LE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+    default:
+      break;
+    }
+  
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   &data, 4,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_int64:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @data: a #gint64.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_int64 (GDataOutputStream     *stream,
+                               gint64                 data,
+                               GCancellable          *cancellable,
+                               GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+
+  switch (stream->priv->byte_order)
+    {
+    case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+      data = GINT64_TO_BE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+      data = GINT64_TO_LE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+    default:
+      break;
+    }
+  
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   &data, 8,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_uint64:
+ * @stream: a #GDataOutputStream. a #GDataOutputStream.
+ * @data: a #guint64.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_uint64 (GDataOutputStream     *stream,
+                                guint64                data,
+                                GCancellable          *cancellable,
+                                GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+
+  switch (stream->priv->byte_order)
+    {
+    case G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN:
+      data = GUINT64_TO_BE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_LITTLE_ENDIAN:
+      data = GUINT64_TO_LE (data);
+      break;
+    case G_DATA_STREAM_BYTE_ORDER_HOST_ENDIAN:
+    default:
+      break;
+    }
+  
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   &data, 8,
+                                   &bytes_written,
+                                   cancellable, error);
+}
+
+/**
+ * g_data_output_stream_put_string:
+ * @stream: a #GDataOutputStream.
+ * @str: a string.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if @data was successfully added to the @stream.
+ **/
+gboolean
+g_data_output_stream_put_string (GDataOutputStream     *stream,
+                                const char            *str,
+                                GCancellable          *cancellable,
+                                GError               **error)
+{
+  gsize bytes_written;
+  
+  g_return_val_if_fail (G_IS_DATA_OUTPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (str != NULL, FALSE);
+
+  return g_output_stream_write_all (G_OUTPUT_STREAM (stream),
+                                   str, strlen (str),
+                                   &bytes_written,
+                                   cancellable, error);
+}  
diff --git a/gio/gdataoutputstream.h b/gio/gdataoutputstream.h
new file mode 100644 (file)
index 0000000..94bdfa5
--- /dev/null
@@ -0,0 +1,108 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DATA_OUTPUT_STREAM_H__
+#define __G_DATA_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gfilteroutputstream.h>
+#include <gio/gdatainputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DATA_OUTPUT_STREAM         (g_data_output_stream_get_type ())
+#define G_DATA_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStream))
+#define G_DATA_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStreamClass))
+#define G_IS_DATA_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DATA_OUTPUT_STREAM))
+#define G_IS_DATA_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DATA_OUTPUT_STREAM))
+#define G_DATA_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DATA_OUTPUT_STREAM, GDataOutputStreamClass))
+
+typedef struct _GDataOutputStream         GDataOutputStream;
+typedef struct _GDataOutputStreamClass    GDataOutputStreamClass;
+typedef struct _GDataOutputStreamPrivate  GDataOutputStreamPrivate;
+
+struct _GDataOutputStream
+{
+  GFilterOutputStream parent;
+
+  /*< private >*/
+  GDataOutputStreamPrivate *priv;
+};
+
+struct _GDataOutputStreamClass
+{
+ GFilterOutputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+
+GType          g_data_output_stream_get_type   (void) G_GNUC_CONST;
+GDataOutputStream*  g_data_output_stream_new        (GOutputStream *base_stream);
+
+void                 g_data_output_stream_set_byte_order (GDataOutputStream     *data_stream,
+                                                         GDataStreamByteOrder   order);
+GDataStreamByteOrder g_data_output_stream_get_byte_order (GDataOutputStream     *stream);
+void                 g_data_output_stream_set_expand_buffer (GDataOutputStream     *data_stream,
+                                                            gboolean               expand_buffer);
+
+gboolean             g_data_output_stream_put_byte       (GDataOutputStream     *data_stream,
+                                                         guchar                 data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_int16      (GDataOutputStream     *stream,
+                                                         gint16                 data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_uint16     (GDataOutputStream     *stream,
+                                                         guint16                data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_int32      (GDataOutputStream     *stream,
+                                                         gint32                 data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_uint32     (GDataOutputStream     *stream,
+                                                         guint32                data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_int64      (GDataOutputStream     *stream,
+                                                         gint64                 data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_uint64     (GDataOutputStream     *stream,
+                                                         guint64                data,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+gboolean             g_data_output_stream_put_string     (GDataOutputStream     *stream,
+                                                         const char            *str,
+                                                         GCancellable          *cancellable,
+                                                         GError               **error);
+
+G_END_DECLS
+
+#endif /* __G_DATA_OUTPUT_STREAM_H__ */
diff --git a/gio/gdesktopappinfo.c b/gio/gdesktopappinfo.c
new file mode 100644 (file)
index 0000000..e8e77cf
--- /dev/null
@@ -0,0 +1,2185 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright Â© 2007 Ryan Lortie
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include "gcontenttypeprivate.h"
+#include "gdesktopappinfo.h"
+#include "gioerror.h"
+#include "gthemedicon.h"
+#include "gfileicon.h"
+#include <glib/gstdio.h>
+#include "glibintl.h"
+
+#define DEFAULT_APPLICATIONS_GROUP  "Default Applications" 
+#define MIME_CACHE_GROUP            "MIME Cache"
+
+static void g_desktop_app_info_iface_init (GAppInfoIface *iface);
+
+static GList *get_all_desktop_entries_for_mime_type (const char *base_mime_type);
+static void mime_info_cache_reload (const char *dir);
+
+struct _GDesktopAppInfo
+{
+  GObject parent_instance;
+
+  char *desktop_id;
+  char *filename;
+
+  char *name;
+  /* FIXME: what about GenericName ? */
+  char *comment;
+  char *icon_name;
+  GIcon *icon;
+  char **only_show_in;
+  char **not_show_in;
+  char *try_exec;
+  char *exec;
+  char *binary;
+  char *path;
+
+  guint nodisplay       : 1;
+  guint hidden          : 1;
+  guint terminal        : 1;
+  guint startup_notify  : 1;
+  /* FIXME: what about StartupWMClass ? */
+};
+
+G_DEFINE_TYPE_WITH_CODE (GDesktopAppInfo, g_desktop_app_info, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
+                                               g_desktop_app_info_iface_init))
+
+static gpointer
+search_path_init (gpointer data)
+{
+  char **args = NULL;
+  const char * const *data_dirs;
+  const char *user_data_dir;
+  int i, length, j;
+
+  data_dirs = g_get_system_data_dirs ();
+  length = g_strv_length ((char **) data_dirs);
+  
+  args = g_new (char *, length + 2);
+  
+  j = 0;
+  user_data_dir = g_get_user_data_dir ();
+  args[j++] = g_build_filename (user_data_dir, "applications", NULL);
+  for (i = 0; i < length; i++)
+    args[j++] = g_build_filename (data_dirs[i],
+                                 "applications", NULL);
+  args[j++] = NULL;
+  
+  return args;
+}
+  
+static const char * const *
+get_applications_search_path (void)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  return g_once (&once_init, search_path_init, NULL);
+}
+
+static void
+g_desktop_app_info_finalize (GObject *object)
+{
+  GDesktopAppInfo *info;
+
+  info = G_DESKTOP_APP_INFO (object);
+
+  g_free (info->desktop_id);
+  g_free (info->filename);
+  g_free (info->name);
+  g_free (info->comment);
+  g_free (info->icon_name);
+  if (info->icon)
+    g_object_unref (info->icon);
+  g_strfreev (info->only_show_in);
+  g_strfreev (info->not_show_in);
+  g_free (info->try_exec);
+  g_free (info->exec);
+  g_free (info->binary);
+  g_free (info->path);
+  
+  G_OBJECT_CLASS (g_desktop_app_info_parent_class)->finalize (object);
+}
+
+static void
+g_desktop_app_info_class_init (GDesktopAppInfoClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_desktop_app_info_finalize;
+}
+
+static void
+g_desktop_app_info_init (GDesktopAppInfo *local)
+{
+}
+
+/**
+ * g_desktop_app_info_new_from_filename:
+ * @filename: a string containing a file name.
+ * 
+ * Returns: a new #GDesktopAppInfo or %NULL on error.
+ **/
+GDesktopAppInfo *
+g_desktop_app_info_new_from_filename (const char *filename)
+{
+  GDesktopAppInfo *info;
+  GKeyFile *key_file;
+  char *start_group;
+  char *type;
+  char *try_exec;
+  
+  key_file = g_key_file_new ();
+  
+  if (!g_key_file_load_from_file (key_file,
+                                 filename,
+                                 G_KEY_FILE_NONE,
+                                 NULL))
+    return NULL;
+
+  start_group = g_key_file_get_start_group (key_file);
+  if (start_group == NULL || strcmp (start_group, G_KEY_FILE_DESKTOP_GROUP) != 0)
+    {
+      g_free (start_group);
+      return NULL;
+    }
+  g_free (start_group);
+
+  type = g_key_file_get_string (key_file,
+                                G_KEY_FILE_DESKTOP_GROUP,
+                                G_KEY_FILE_DESKTOP_KEY_TYPE,
+                                NULL);
+  if (type == NULL || strcmp (type, G_KEY_FILE_DESKTOP_TYPE_APPLICATION) != 0)
+    {
+      g_free (type);
+      return NULL;
+    }
+  g_free (type);
+
+  try_exec = g_key_file_get_string (key_file,
+                                    G_KEY_FILE_DESKTOP_GROUP,
+                                    G_KEY_FILE_DESKTOP_KEY_TRY_EXEC,
+                                    NULL);
+  if (try_exec)
+    {
+      char *t;
+      t = g_find_program_in_path (try_exec);
+      if (t == NULL)
+       {
+         g_free (try_exec);
+         return NULL;
+       }
+      g_free (t);
+    }
+
+  info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
+  info->filename = g_strdup (filename);
+
+  info->name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, NULL, NULL);
+  info->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, NULL, NULL);
+  info->nodisplay = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) != FALSE;
+  info->icon_name =  g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ICON, NULL, NULL);
+  info->only_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_ONLY_SHOW_IN, NULL, NULL);
+  info->not_show_in = g_key_file_get_string_list (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NOT_SHOW_IN, NULL, NULL);
+  info->try_exec = try_exec;
+  info->exec = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_EXEC, NULL);
+  info->path = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_PATH, NULL);
+  info->terminal = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TERMINAL, NULL) != FALSE;
+  info->startup_notify = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_STARTUP_NOTIFY, NULL) != FALSE;
+  info->hidden = g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL) != FALSE;
+
+  info->icon = NULL;
+  if (info->icon_name)
+    {
+      if (g_path_is_absolute (info->icon_name))
+       {
+         GFile *file;
+         
+         file = g_file_new_for_path (info->icon_name);
+         info->icon = g_file_icon_new (file);
+         g_object_unref (file);
+       }
+      else
+       info->icon = g_themed_icon_new (info->icon_name);
+    }
+  
+  if (info->exec)
+    {
+      char *p, *start;
+
+      p = info->exec;
+      while (*p == ' ')
+       p++;
+      start = p;
+      while (*p != ' ' && *p != 0)
+       p++;
+      
+      info->binary = g_strndup (start, p - start);
+    }
+  
+  return info;
+}
+
+/**
+ * g_desktop_app_info_new:
+ * @desktop_id:
+ * 
+ * Returns: a new #GDesktopAppInfo.
+ **/
+GDesktopAppInfo *
+g_desktop_app_info_new (const char *desktop_id)
+{
+  GDesktopAppInfo *appinfo;
+  const char * const *dirs;
+  int i;
+
+  dirs = get_applications_search_path ();
+
+  for (i = 0; dirs[i] != NULL; i++)
+    {
+      char *basename;
+      char *filename;
+      char *p;
+
+      filename = g_build_filename (dirs[i], desktop_id, NULL);
+      appinfo = g_desktop_app_info_new_from_filename (filename);
+      g_free (filename);
+      if (appinfo != NULL)
+       {
+         goto found;
+       }
+
+      basename = g_strdup (desktop_id);
+      p = basename;
+      while ((p = strchr (p, '-')) != NULL)
+       {
+         *p = '/';
+         
+         filename = g_build_filename (dirs[i], basename, NULL);
+         appinfo = g_desktop_app_info_new_from_filename (filename);
+         g_free (filename);
+         if (appinfo != NULL)
+           {
+             g_free (basename);
+             goto found;
+           }
+         *p = '-';
+         p++;
+       }
+    }
+  
+  return NULL;
+
+ found:
+  appinfo->desktop_id = g_strdup (desktop_id);
+
+  if (g_desktop_app_info_get_is_hidden (appinfo))
+    {
+      g_object_unref (appinfo);
+      appinfo = NULL;
+    }
+  
+  return appinfo;
+}
+
+static GAppInfo *
+g_desktop_app_info_dup (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  GDesktopAppInfo *new_info;
+  
+  new_info = g_object_new (G_TYPE_DESKTOP_APP_INFO, NULL);
+
+  new_info->filename = g_strdup (info->filename);
+  new_info->desktop_id = g_strdup (info->desktop_id);
+  
+  new_info->name = g_strdup (info->name);
+  new_info->comment = g_strdup (info->comment);
+  new_info->nodisplay = info->nodisplay;
+  new_info->icon_name = g_strdup (info->icon_name);
+  new_info->icon = g_object_ref (info->icon);
+  new_info->only_show_in = g_strdupv (info->only_show_in);
+  new_info->not_show_in = g_strdupv (info->not_show_in);
+  new_info->try_exec = g_strdup (info->try_exec);
+  new_info->exec = g_strdup (info->exec);
+  new_info->binary = g_strdup (info->binary);
+  new_info->path = g_strdup (info->path);
+  new_info->hidden = info->hidden;
+  new_info->terminal = info->terminal;
+  new_info->startup_notify = info->startup_notify;
+  
+  return G_APP_INFO (new_info);
+}
+
+static gboolean
+g_desktop_app_info_equal (GAppInfo *appinfo1,
+                         GAppInfo *appinfo2)
+{
+  GDesktopAppInfo *info1 = G_DESKTOP_APP_INFO (appinfo1);
+  GDesktopAppInfo *info2 = G_DESKTOP_APP_INFO (appinfo2);
+
+  if (info1->desktop_id == NULL ||
+      info2->desktop_id == NULL)
+    return FALSE;
+
+  return strcmp (info1->desktop_id, info2->desktop_id) == 0;
+}
+
+static const char *
+g_desktop_app_info_get_id (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+  return info->desktop_id;
+}
+
+static const char *
+g_desktop_app_info_get_name (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+  if (info->name == NULL)
+    return _("Unnamed");
+  return info->name;
+}
+
+/**
+ * g_desktop_app_info_get_is_hidden:
+ * @info:
+ * 
+ * Returns: %TRUE if hidden, %FALSE otherwise. 
+ **/
+gboolean
+g_desktop_app_info_get_is_hidden (GDesktopAppInfo *info)
+{
+  return info->hidden;
+}
+
+static const char *
+g_desktop_app_info_get_description (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  
+  return info->comment;
+}
+
+static const char *
+g_desktop_app_info_get_executable (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  
+  return info->binary;
+}
+
+static GIcon *
+g_desktop_app_info_get_icon (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+  return info->icon;
+}
+
+static char *
+expand_macro_single (char macro, GFile *file)
+{
+  char *result = NULL;
+  char *uri, *path;
+
+  path = g_file_get_path (file);
+  uri = g_file_get_uri (file);
+  
+  switch (macro)
+    {
+    case 'u':
+    case 'U':  
+      result = g_shell_quote (uri);
+      break;
+    case 'f':
+    case 'F':
+      if (path)
+       result = g_shell_quote (path);
+      break;
+    case 'd':
+    case 'D':
+      if (path)
+       result = g_shell_quote (g_path_get_dirname (path));
+      break;
+    case 'n':
+    case 'N':
+      if (path)
+       result = g_shell_quote (g_path_get_basename (path));
+      break;
+    }
+
+  g_free (path);
+  g_free (uri);
+  
+  return result;
+}
+
+static void
+expand_macro (char macro, GString *exec, GDesktopAppInfo *info, GList **file_list)
+{
+  GList *files = *file_list;
+  char *expanded;
+  
+  g_return_if_fail (exec != NULL);
+  
+  switch (macro)
+    {
+    case 'u':
+    case 'f':
+    case 'd':
+    case 'n':
+      if (files)
+       {
+         expanded = expand_macro_single (macro, files->data);
+         if (expanded)
+           {
+             g_string_append (exec, expanded);
+             g_free (expanded);
+           }
+         files = files->next;
+       }
+
+      break;
+
+    case 'U':  
+    case 'F':
+    case 'D':
+    case 'N':
+      while (files)
+       {
+         expanded = expand_macro_single (macro, files->data);
+         if (expanded)
+           {
+             g_string_append (exec, expanded);
+             g_free (expanded);
+           }
+         
+         files = files->next;
+         
+         if (files != NULL && expanded)
+           g_string_append_c (exec, ' ');
+       }
+
+      break;
+
+    case 'i':
+      if (info->icon_name)
+       {
+         g_string_append (exec, "--icon ");
+         g_string_append (exec, info->icon_name);
+       }
+      break;
+
+    case 'c':
+      if (info->name) 
+       g_string_append (exec, info->name);
+      break;
+
+    case 'k':
+      if (info->filename) 
+       g_string_append (exec, info->filename);
+      break;
+
+    case 'm': /* deprecated */
+      break;
+
+    case '%':
+      g_string_append_c (exec, '%');
+      break;
+    }
+  
+  *file_list = files;
+}
+
+static gboolean
+expand_application_parameters (GDesktopAppInfo *info,
+                              GList         **files,
+                              int            *argc,
+                              char         ***argv,
+                              GError        **error)
+{
+  GList *file_list = *files;
+  const char *p = info->exec;
+  GString *expanded_exec = g_string_new (NULL);
+  gboolean res;
+  
+  if (info->exec == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                  _("Desktop file didn't specify Exec field"));
+      return FALSE;
+    }
+  
+  while (*p)
+    {
+      if (p[0] == '%' && p[1] != '\0')
+       {
+         expand_macro (p[1], expanded_exec, info, files);
+         p++;
+       }
+      else
+       g_string_append_c (expanded_exec, *p);
+      
+      p++;
+    }
+  
+  /* No file substitutions */
+  if (file_list == *files && file_list != NULL)
+    {
+      /* If there is no macro default to %f. This is also what KDE does */
+      g_string_append_c (expanded_exec, ' ');
+      expand_macro ('f', expanded_exec, info, files);
+    }
+  
+  res = g_shell_parse_argv (expanded_exec->str, argc, argv, error);
+  g_string_free (expanded_exec, TRUE);
+  return res;
+}
+
+static gboolean
+prepend_terminal_to_vector (int *argc,
+                           char ***argv)
+{
+#ifndef G_OS_WIN32
+  char **real_argv;
+  int real_argc;
+  int i, j;
+  char **term_argv = NULL;
+  int term_argc = 0;
+  char *check;
+  char **the_argv;
+  
+  g_return_val_if_fail (argc != NULL, FALSE);
+  g_return_val_if_fail (argv != NULL, FALSE);
+       
+  /* sanity */
+  if(*argv == NULL)
+    *argc = 0;
+  
+  the_argv = *argv;
+
+  /* compute size if not given */
+  if (*argc < 0)
+    {
+      for (i = 0; the_argv[i] != NULL; i++)
+       ;
+      *argc = i;
+    }
+  
+  term_argc = 2;
+  term_argv = g_new0 (char *, 3);
+
+  check = g_find_program_in_path ("gnome-terminal");
+  if (check != NULL)
+    {
+      term_argv[0] = check;
+      /* Note that gnome-terminal takes -x and
+       * as -e in gnome-terminal is broken we use that. */
+      term_argv[1] = g_strdup ("-x");
+    }
+  else
+    {
+      if (check == NULL)
+       check = g_find_program_in_path ("nxterm");
+      if (check == NULL)
+       check = g_find_program_in_path ("color-xterm");
+      if (check == NULL)
+       check = g_find_program_in_path ("rxvt");
+      if (check == NULL)
+       check = g_find_program_in_path ("xterm");
+      if (check == NULL)
+       check = g_find_program_in_path ("dtterm");
+      if (check == NULL)
+       {
+         check = g_strdup ("xterm");
+         g_warning ("couldn't find a terminal, falling back to xterm");
+       }
+      term_argv[0] = check;
+      term_argv[1] = g_strdup ("-e");
+    }
+
+  real_argc = term_argc + *argc;
+  real_argv = g_new (char *, real_argc + 1);
+  
+  for (i = 0; i < term_argc; i++)
+    real_argv[i] = term_argv[i];
+  
+  for (j = 0; j < *argc; j++, i++)
+    real_argv[i] = (char *)the_argv[j];
+  
+  real_argv[i] = NULL;
+  
+  g_free (*argv);
+  *argv = real_argv;
+  *argc = real_argc;
+  
+  /* we use g_free here as we sucked all the inner strings
+   * out from it into real_argv */
+  g_free (term_argv);
+  return TRUE;
+#else
+  return FALSE;
+#endif /* G_OS_WIN32 */
+}
+
+/* '=' is the new '\0'.
+ * DO NOT CALL unless at least one string ends with '='
+ */
+static gboolean
+is_env (const char *a,
+       const char *b)
+{
+  while (*a == *b)
+  {
+    if (*a == 0 || *b == 0)
+      return FALSE;
+    
+    if (*a == '=')
+      return TRUE;
+
+    a++;
+    b++;
+  }
+
+  return FALSE;
+}
+
+/* free with g_strfreev */
+static char **
+replace_env_var (char **old_environ,
+                const char *env_var,
+                const char *new_value)
+{
+  int length, new_length;
+  int index, new_index;
+  char **new_environ;
+  int i, new_i;
+
+  /* do two things at once:
+   *  - discover the length of the environment ('length')
+   *  - find the location (if any) of the env var ('index')
+   */
+  index = -1;
+  for (length = 0; old_environ[length]; length++)
+    {
+      /* if we already have it in our environment, replace */
+      if (is_env (old_environ[length], env_var))
+       index = length;
+    }
+
+  
+  /* no current env var, no desired env value.
+   * this is easy :)
+   */
+  if (new_value == NULL && index == -1)
+    return old_environ;
+
+  /* in all cases now, we will be using a modified environment.
+   * determine its length and allocated it.
+   * 
+   * after this block:
+   *   new_index   = location to insert, if any
+   *   new_length  = length of the new array
+   *   new_environ = the pointer array for the new environment
+   */
+  
+  if (new_value == NULL && index >= 0)
+    {
+      /* in this case, we will be removing an entry */
+      new_length = length - 1;
+      new_index = -1;
+    }
+  else if (new_value != NULL && index < 0)
+    {
+      /* in this case, we will be adding an entry to the end */
+      new_length = length + 1;
+      new_index = length;
+    }
+  else
+    /* in this case, we will be replacing the existing entry */
+    {
+      new_length = length;
+      new_index = index;
+    }
+
+  new_environ = g_malloc (sizeof (char *) * (new_length + 1));
+  new_environ[new_length] = NULL;
+
+  /* now we do the copying.
+   * for each entry in the new environment, we decide what to do
+   */
+  
+  i = 0;
+  for (new_i = 0; new_i < new_length; new_i++)
+    {
+      if (new_i == new_index)
+       {
+         /* insert our new item */
+         new_environ[new_i] = g_strconcat (env_var,
+                                           "=",
+                                           new_value,
+                                           NULL);
+         
+         /* if we had an old entry, skip it now */
+         if (index >= 0)
+           i++;
+       }
+      else
+       {
+         /* if this is the old DESKTOP_STARTUP_ID, skip it */
+         if (i == index)
+           i++;
+         
+         /* copy an old item */
+         new_environ[new_i] = g_strdup (old_environ[i]);
+         i++;
+       }
+    }
+
+  g_strfreev (old_environ);
+  
+  return new_environ;
+}
+
+static GList *
+dup_list_segment (GList *start,
+                 GList *end)
+{
+  GList *res;
+
+  res = NULL;
+  while (start != NULL && start != end)
+    {
+      res = g_list_prepend (res, start->data);
+      start = start->next;
+    }
+
+  return g_list_reverse (res);
+}
+
+static gboolean
+g_desktop_app_info_launch (GAppInfo                *appinfo,
+                          GList                   *files,
+                          GAppLaunchContext       *launch_context,
+                          GError                 **error)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  gboolean completed = FALSE;
+  GList *old_files;
+  GList *launched_files;
+  char **envp;
+  char **argv;
+  int argc;
+  char *display;
+  char *sn_id;
+
+  g_return_val_if_fail (appinfo != NULL, FALSE);
+
+  argv = NULL;
+  envp = NULL;
+      
+  do 
+    {
+      old_files = files;
+      if (!expand_application_parameters (info, &files,
+                                         &argc, &argv, error))
+       goto out;
+      
+      if (info->terminal && !prepend_terminal_to_vector (&argc, &argv))
+       {
+         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                      _("Unable to find terminal required for application"));
+         goto out;
+       }
+
+      sn_id = NULL;
+      if (launch_context)
+       {
+         launched_files = dup_list_segment (old_files, files);
+         
+         display = g_app_launch_context_get_display (launch_context,
+                                                     appinfo,
+                                                     launched_files);
+
+         sn_id = NULL;
+         if (info->startup_notify)
+           sn_id = g_app_launch_context_get_startup_notify_id (launch_context,
+                                                               appinfo,
+                                                               launched_files);
+         
+         if (display || sn_id)
+           {
+             envp = g_listenv ();
+             
+             if (display)
+               envp = replace_env_var (envp,
+                                       "DISPLAY",
+                                       display);
+             
+             if (sn_id)
+               envp = replace_env_var (envp,
+                                       "DESKTOP_STARTUP_ID",
+                                       sn_id);
+           }
+
+         g_free (display);
+         
+         g_list_free (launched_files);
+       }
+      
+      if (!g_spawn_async (info->path,  /* working directory */
+                         argv,
+                         envp,
+                         G_SPAWN_SEARCH_PATH /* flags */,
+                         NULL /* child_setup */,
+                         NULL /* data */,
+                         NULL /* child_pid */,
+                         error))
+       {
+         if (sn_id)
+           {
+             g_app_launch_context_launch_failed (launch_context, sn_id);
+             g_free (sn_id);
+           }
+         goto out;
+       }
+
+      
+      g_free (sn_id);
+      
+      g_strfreev (envp);
+      g_strfreev (argv);
+      envp = NULL;
+      argv = NULL;
+    }
+  while (files != NULL);
+
+  completed = TRUE;
+
+ out:
+  g_strfreev (argv);
+  g_strfreev (envp);
+
+  return completed;
+}
+
+static gboolean
+g_desktop_app_info_supports_uris (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  
+  return
+    (strstr (info->exec, "%u") != NULL) ||
+    (strstr (info->exec, "%U") != NULL);
+}
+
+static gboolean
+g_desktop_app_info_supports_xdg_startup_notify (GAppInfo *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  
+  return info->startup_notify;
+}
+static gboolean
+g_desktop_app_info_launch_uris (GAppInfo *appinfo,
+                               GList *uris,
+                               GAppLaunchContext *launch_context,
+                               GError **error)
+{
+  GList *files;
+  GFile *file;
+  gboolean res;
+
+  files = NULL;
+  while (uris)
+    {
+      file = g_file_new_for_uri (uris->data);
+      if (file == NULL)
+       g_warning ("Invalid uri passed to g_desktop_app_info_launch_uris");
+      
+      if (file)
+       files = g_list_prepend (files, file);
+    }
+  
+  files = g_list_reverse (files);
+  
+  res = g_desktop_app_info_launch (appinfo, files, launch_context, error);
+  
+  g_list_foreach  (files, (GFunc)g_object_unref, NULL);
+  g_list_free (files);
+  
+  return res;
+}
+
+static gboolean
+g_desktop_app_info_should_show (GAppInfo *appinfo,
+                               const char *desktop_env)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  gboolean found;
+  int i;
+
+  if (info->nodisplay)
+    return FALSE;
+
+  if (info->only_show_in)
+    {
+      if (desktop_env == NULL)
+       return FALSE;
+      
+      found = FALSE;
+      for (i = 0; info->only_show_in[i] != NULL; i++)
+       {
+         if (strcmp (info->only_show_in[i], desktop_env) == 0)
+           {
+             found = TRUE;
+             break;
+           }
+       }
+      if (!found)
+       return FALSE;
+    }
+
+  if (info->not_show_in && desktop_env)
+    {
+      for (i = 0; info->not_show_in[i] != NULL; i++)
+       {
+         if (strcmp (info->not_show_in[i], desktop_env) == 0)
+           return FALSE;
+       }
+    }
+  
+  return TRUE;
+}
+
+typedef enum {
+  APP_DIR,
+  MIMETYPE_DIR
+} DirType;
+
+static char *
+ensure_dir (DirType type,
+            GError **error)
+{
+  char *path, *display_name;
+  int err;
+
+  if (type == APP_DIR)
+    {
+      path = g_build_filename (g_get_user_data_dir (), "applications", NULL);
+    }
+  else
+    {
+      path = g_build_filename (g_get_user_data_dir (), "mime", "packages", NULL);
+    }
+
+  errno = 0;
+  if (g_mkdir_with_parents (path, 0700) == 0)
+    return path;
+
+  err = errno;
+  display_name = g_filename_display_name (path);
+  if (type == APP_DIR)
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (err),
+                   _("Can't create user application configuration folder %s: %s"),
+                   display_name, g_strerror (err));
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, g_io_error_from_errno (err),
+                   _("Can't create user mime configuration folder %s: %s"),
+                   display_name, g_strerror (err));
+    }
+
+  g_free (display_name);
+  g_free (path);
+
+  return NULL;
+}
+
+static gboolean
+update_default_list (const char *desktop_id, const char *content_type, gboolean add, GError **error)
+{
+  char *dirname, *filename;
+  GKeyFile *key_file;
+  gboolean load_succeeded, res;
+  char **old_list;
+  char **list;
+  gsize length, data_size;
+  char *data;
+  int i, j;
+
+  dirname = ensure_dir (APP_DIR, error);
+  if (!dirname)
+    return FALSE;
+
+  filename = g_build_filename (dirname, "defaults.list", NULL);
+  g_free (dirname);
+
+  key_file = g_key_file_new ();
+  load_succeeded = g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, NULL);
+  if (!load_succeeded || !g_key_file_has_group (key_file, DEFAULT_APPLICATIONS_GROUP))
+    {
+      g_key_file_free (key_file);
+      key_file = g_key_file_new ();
+    }
+
+  length = 0;
+  old_list = g_key_file_get_string_list (key_file, DEFAULT_APPLICATIONS_GROUP,
+                                        content_type, &length, NULL);
+
+  list = g_new (char *, 1 + length + 1);
+
+  i = 0;
+  if (add)
+    list[i++] = g_strdup (desktop_id);
+  if (old_list)
+    {
+      for (j = 0; old_list[j] != NULL; j++)
+       {
+         if (strcmp (old_list[j], desktop_id) != 0)
+           list[i++] = g_strdup (old_list[j]);
+       }
+    }
+  list[i] = NULL;
+  
+  g_strfreev (old_list);
+
+  g_key_file_set_string_list (key_file,
+                             DEFAULT_APPLICATIONS_GROUP,
+                             content_type,
+                             (const char * const *)list, i);
+
+  g_strfreev (list);
+  
+  data = g_key_file_to_data (key_file, &data_size, error);
+  g_key_file_free (key_file);
+  
+  res = g_file_set_contents (filename, data, data_size, error);
+
+  mime_info_cache_reload (NULL);
+                         
+  g_free (filename);
+  g_free (data);
+  
+  return res;
+}
+
+static gboolean
+g_desktop_app_info_set_as_default_for_type (GAppInfo    *appinfo,
+                                           const char  *content_type,
+                                           GError     **error)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+
+  if (!g_app_info_add_supports_type (appinfo, content_type, error))
+    return FALSE;
+  
+  return update_default_list (info->desktop_id, content_type, TRUE, error);
+}
+
+static void
+update_program_done (GPid     pid,
+                    gint     status,
+                    gpointer data)
+{
+  /* Did the application exit correctly */
+  if (WIFEXITED (status) &&
+      WEXITSTATUS (status) == 0)
+    {
+      /* Here we could clean out any caches in use */
+    }
+}
+
+static void
+run_update_command (char *command,
+                   char *subdir)
+{
+       char *argv[3] = {
+               NULL,
+               NULL,
+               NULL,
+       };
+       GPid pid = 0;
+       GError *error = NULL;
+
+       argv[0] = command;
+       argv[1] = g_build_filename (g_get_user_data_dir (), subdir, NULL);
+
+       if (g_spawn_async ("/", argv,
+                          NULL,       /* envp */
+                          G_SPAWN_SEARCH_PATH |
+                          G_SPAWN_STDOUT_TO_DEV_NULL |
+                          G_SPAWN_STDERR_TO_DEV_NULL |
+                          G_SPAWN_DO_NOT_REAP_CHILD,
+                          NULL, NULL, /* No setup function */
+                          &pid,
+                          NULL)) 
+         g_child_watch_add (pid, update_program_done, NULL);
+       else
+         {
+           /* If we get an error at this point, it's quite likely the user doesn't
+            * have an installed copy of either 'update-mime-database' or
+            * 'update-desktop-database'.  I don't think we want to popup an error
+            * dialog at this point, so we just do a g_warning to give the user a
+            * chance of debugging it.
+            */
+           g_warning ("%s", error->message);
+         }
+       
+       g_free (argv[1]);
+}
+
+static gboolean
+g_desktop_app_info_set_as_default_for_extension (GAppInfo           *appinfo,
+                                                const char         *extension,
+                                                GError            **error)
+{
+  char *filename, *basename, *mimetype;
+  char *dirname;
+  gboolean res;
+
+  dirname = ensure_dir (MIMETYPE_DIR, error);
+  if (!dirname)
+    return FALSE;
+  
+  basename = g_strdup_printf ("user-extension-%s.xml", extension);
+  filename = g_build_filename (dirname, basename, NULL);
+  g_free (basename);
+  g_free (dirname);
+
+  mimetype = g_strdup_printf ("application/x-extension-%s", extension);
+  
+  if (!g_file_test (filename, G_FILE_TEST_EXISTS)) {
+    char *contents;
+
+    contents =
+      g_strdup_printf ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+                      "<mime-info xmlns=\"http://www.freedesktop.org/standards/shared-mime-info\">\n"
+                      " <mime-type type=\"%s\">\n"
+                      "  <comment>%s document</comment>\n"
+                      "  <glob pattern=\"*.%s\"/>\n"
+                      " </mime-type>\n"
+                      "</mime-info>\n", mimetype, extension, extension);
+
+    g_file_set_contents (filename, contents, -1, NULL);
+    g_free (contents);
+
+    run_update_command ("update-mime-database", "mime");
+  }
+  g_free (filename);
+  
+  res = g_desktop_app_info_set_as_default_for_type (appinfo,
+                                                   mimetype,
+                                                   error);
+
+  g_free (mimetype);
+  
+  return res;
+}
+
+static gboolean
+g_desktop_app_info_add_supports_type (GAppInfo           *appinfo,
+                                     const char         *content_type,
+                                     GError            **error)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  GKeyFile *keyfile;
+  char *new_mimetypes, *old_mimetypes, *content;
+  char *dirname;
+  char *filename;
+
+  keyfile = g_key_file_new ();
+  if (!g_key_file_load_from_file (keyfile, info->filename,
+                                 G_KEY_FILE_KEEP_COMMENTS |
+                                 G_KEY_FILE_KEEP_TRANSLATIONS, error))
+    {
+      g_key_file_free (keyfile);
+      return FALSE;
+    }
+
+  old_mimetypes = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL);
+  new_mimetypes = g_strconcat (content_type, ";", old_mimetypes, NULL);
+  g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, new_mimetypes);
+  g_free (old_mimetypes);
+  g_free (new_mimetypes);
+
+  content = g_key_file_to_data (keyfile, NULL, NULL);
+  g_key_file_free (keyfile);
+
+  dirname = ensure_dir (APP_DIR, error);
+  if (!dirname)
+    {
+      g_free (content);
+      return FALSE;
+    }
+  
+  filename = g_build_filename (dirname, info->desktop_id, NULL);
+  g_free (dirname);
+  
+  if (!g_file_set_contents (filename, content, -1, error))
+    {
+      g_free (filename);
+      g_free (content);
+      return FALSE;
+    }
+  g_free (filename);
+  g_free (content);
+
+  run_update_command ("update-desktop-database", "applications");
+  return TRUE;
+}
+
+static gboolean
+g_desktop_app_info_can_remove_supports_type (GAppInfo           *appinfo)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  char *user_dirname;
+  
+  user_dirname = g_build_filename (g_get_user_data_dir (), "applications", NULL);
+  return g_str_has_prefix (info->filename, user_dirname);
+}
+
+static gboolean
+g_desktop_app_info_remove_supports_type (GAppInfo           *appinfo,
+                                        const char         *content_type,
+                                        GError            **error)
+{
+  GDesktopAppInfo *info = G_DESKTOP_APP_INFO (appinfo);
+  GKeyFile *keyfile;
+  char *new_mimetypes, *old_mimetypes, *content;
+  char *found;
+  char *filename;
+  char *dirname;
+
+  keyfile = g_key_file_new ();
+  if (!g_key_file_load_from_file (keyfile, info->filename,
+                                 G_KEY_FILE_KEEP_COMMENTS |
+                                 G_KEY_FILE_KEEP_TRANSLATIONS, error))
+    {
+      g_key_file_free (keyfile);
+      return FALSE;
+    }
+
+  old_mimetypes = g_key_file_get_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, NULL);
+  new_mimetypes = g_strdup (old_mimetypes);
+  found = NULL;
+  if (new_mimetypes)
+    found = strstr (new_mimetypes, content_type);
+  if (found && *(found + strlen (content_type)) == ';')
+    {
+      char *rest = found + strlen (content_type) + 1;
+      memmove (found, rest, strlen (rest) + 1);
+    }
+  g_key_file_set_string (keyfile, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_MIME_TYPE, new_mimetypes);
+  g_free (old_mimetypes);
+  g_free (new_mimetypes);
+
+  content = g_key_file_to_data (keyfile, NULL, NULL);
+  g_key_file_free (keyfile);
+
+  dirname = ensure_dir (APP_DIR, error);
+  if (!dirname)
+    {
+      g_free (content);
+      return FALSE;
+    }
+  
+  filename = g_build_filename (dirname, info->desktop_id, NULL);
+  g_free (dirname);
+  if (!g_file_set_contents (filename, content, -1, error))
+    {
+      g_free (filename);
+      g_free (content);
+      return FALSE;
+    }
+  g_free (filename);
+  g_free (content);
+
+  run_update_command ("update-desktop-database", "applications");
+
+  return update_default_list (info->desktop_id, content_type, FALSE, error);
+}
+
+/**
+ * g_app_info_create_from_commandline:
+ * @commandline:
+ * @application_name:
+ * @flags:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: new #GAppInfo for given command.
+ **/
+GAppInfo *
+g_app_info_create_from_commandline (const char *commandline,
+                                   const char *application_name,
+                                   GAppInfoCreateFlags flags,
+                                   GError **error)
+{
+  GKeyFile *key_file;
+  char *dirname;
+  char **split;
+  char *basename, *exec, *filename, *comment;
+  char *data, *desktop_id;
+  gsize data_size;
+  int fd;
+  GDesktopAppInfo *info;
+  gboolean res;
+
+  dirname = ensure_dir (APP_DIR, error);
+  if (!dirname)
+    return NULL;
+  
+  key_file = g_key_file_new ();
+
+  g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                        "Encoding", "UTF-8");
+  g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                        G_KEY_FILE_DESKTOP_KEY_VERSION, "1.0");
+  g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                        G_KEY_FILE_DESKTOP_KEY_TYPE,
+                         G_KEY_FILE_DESKTOP_TYPE_APPLICATION);
+  if (flags & G_APP_INFO_CREATE_NEEDS_TERMINAL) 
+    g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                           G_KEY_FILE_DESKTOP_KEY_TERMINAL, TRUE);
+
+  exec = g_strconcat (commandline, " %f", NULL);
+  g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                        G_KEY_FILE_DESKTOP_KEY_EXEC, exec);
+  g_free (exec);
+
+  /* FIXME: this should be more robust. Maybe g_shell_parse_argv and use argv[0] */
+  split = g_strsplit (commandline, " ", 2);
+  basename = g_path_get_basename (split[0]);
+  g_strfreev (split);
+  g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                        G_KEY_FILE_DESKTOP_KEY_NAME, application_name?application_name:basename);
+
+  comment = g_strdup_printf (_("Custom definition for %s"), basename);
+  g_key_file_set_string (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                        G_KEY_FILE_DESKTOP_KEY_COMMENT, comment);
+  g_free (comment);
+  
+  g_key_file_set_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP,
+                         G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, TRUE);
+
+  data = g_key_file_to_data (key_file, &data_size, NULL);
+  g_key_file_free (key_file);
+
+  desktop_id = g_strdup_printf ("userapp-%s-XXXXXX.desktop", basename);
+  g_free (basename);
+  filename = g_build_filename (dirname, desktop_id, NULL);
+  g_free (desktop_id);
+  g_free (dirname);
+  
+  fd = g_mkstemp (filename);
+  if (fd == -1)
+    {
+      char *display_name;
+
+      display_name = g_filename_display_name (filename);
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                  _("Can't create user desktop file %s"), display_name);
+      g_free (display_name);
+      g_free (filename);
+      g_free (data);
+      return NULL;
+    }
+
+  desktop_id = g_path_get_basename (filename);
+
+  close (fd);
+  
+  res = g_file_set_contents (filename, data, data_size, error);
+  if (!res)
+    {
+      g_free (desktop_id);
+      g_free (filename);
+      return NULL;
+    }
+
+  run_update_command ("update-desktop-database", "applications");
+  
+  info = g_desktop_app_info_new_from_filename (filename);
+  g_free (filename);
+  if (info == NULL) 
+    g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                _("Can't load just created desktop file"));
+  else
+    info->desktop_id = g_strdup (desktop_id);
+    
+  g_free (desktop_id);
+  
+  return G_APP_INFO (info);
+}
+
+
+static void
+g_desktop_app_info_iface_init (GAppInfoIface *iface)
+{
+  iface->dup = g_desktop_app_info_dup;
+  iface->equal = g_desktop_app_info_equal;
+  iface->get_id = g_desktop_app_info_get_id;
+  iface->get_name = g_desktop_app_info_get_name;
+  iface->get_description = g_desktop_app_info_get_description;
+  iface->get_executable = g_desktop_app_info_get_executable;
+  iface->get_icon = g_desktop_app_info_get_icon;
+  iface->launch = g_desktop_app_info_launch;
+  iface->supports_uris = g_desktop_app_info_supports_uris;
+  iface->supports_xdg_startup_notify = g_desktop_app_info_supports_xdg_startup_notify;
+  iface->launch_uris = g_desktop_app_info_launch_uris;
+  iface->should_show = g_desktop_app_info_should_show;
+  iface->set_as_default_for_type = g_desktop_app_info_set_as_default_for_type;
+  iface->set_as_default_for_extension = g_desktop_app_info_set_as_default_for_extension;
+  iface->add_supports_type = g_desktop_app_info_add_supports_type;
+  iface->can_remove_supports_type = g_desktop_app_info_can_remove_supports_type;
+  iface->remove_supports_type = g_desktop_app_info_remove_supports_type;
+}
+
+static gboolean
+app_info_in_list (GAppInfo *info, GList *l)
+{
+  while (l != NULL)
+    {
+      if (g_app_info_equal (info, l->data))
+       return TRUE;
+      l = l->next;
+    }
+  return FALSE;
+}
+
+
+/**
+ * g_app_info_get_all_for_type:
+ * @content_type:
+ * 
+ * Returns: #GList of #GAppInfo s for given @content_type.
+ **/
+GList *
+g_app_info_get_all_for_type (const char *content_type)
+{
+  GList *desktop_entries, *l;
+  GList *infos;
+  GDesktopAppInfo *info;
+  
+  desktop_entries = get_all_desktop_entries_for_mime_type (content_type);
+
+  infos = NULL;
+  for (l = desktop_entries; l != NULL; l = l->next)
+    {
+      char *desktop_entry = l->data;
+
+      info = g_desktop_app_info_new (desktop_entry);
+      if (info)
+       {
+         if (app_info_in_list (G_APP_INFO (info), infos))
+           g_object_unref (info);
+         else
+           infos = g_list_prepend (infos, info);
+       }
+      g_free (desktop_entry);
+    }
+
+  g_list_free (desktop_entries);
+  
+  return g_list_reverse (infos);
+}
+
+
+/**
+ * g_app_info-get_default_for_type:
+ * @content_type:
+ * @must_support_uris:
+ * 
+ * Returns: #GAppInfo for given @content_type.
+ **/
+GAppInfo *
+g_app_info_get_default_for_type (const char *content_type,
+                                gboolean must_support_uris)
+{
+  GList *desktop_entries, *l;
+  GAppInfo *info;
+  
+  desktop_entries = get_all_desktop_entries_for_mime_type (content_type);
+
+  info = NULL;
+  for (l = desktop_entries; l != NULL; l = l->next)
+    {
+      char *desktop_entry = l->data;
+
+      info = (GAppInfo *)g_desktop_app_info_new (desktop_entry);
+      if (info)
+       {
+         if (must_support_uris && !g_app_info_supports_uris (info))
+           {
+             g_object_unref (info);
+             info = NULL;
+           }
+         else
+           break;
+       }
+    }
+  
+  g_list_foreach  (desktop_entries, (GFunc)g_free, NULL);
+  g_list_free (desktop_entries);
+  
+  return info;
+}
+
+
+/**
+ * g_app_info_get_default_for_uri_scheme:
+ * @uri_scheme:
+ * 
+ * Returns: #GAppInfo
+ **/
+GAppInfo *
+g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
+{
+  /* TODO: Implement this using giomodules, reading the gconf settings
+   * in /desktop/gnome/url-handlers
+   */
+  return NULL;
+}
+
+
+static void
+get_apps_from_dir (GHashTable *apps, const char *dirname, const char *prefix)
+{
+  GDir *dir;
+  const char *basename;
+  char *filename, *subprefix, *desktop_id;
+  gboolean hidden;
+  GDesktopAppInfo *appinfo;
+  
+  dir = g_dir_open (dirname, 0, NULL);
+  if (dir)
+    {
+      while ((basename = g_dir_read_name (dir)) != NULL)
+       {
+         filename = g_build_filename (dirname, basename, NULL);
+         if (g_str_has_suffix (basename, ".desktop"))
+           {
+             desktop_id = g_strconcat (prefix, basename, NULL);
+
+             /* Use _extended so we catch NULLs too (hidden) */
+             if (!g_hash_table_lookup_extended (apps, desktop_id, NULL, NULL))
+               {
+                 appinfo = g_desktop_app_info_new_from_filename (filename);
+
+                 /* Don't return apps that don't take arguments */
+                 if (appinfo &&
+                     g_desktop_app_info_get_is_hidden (appinfo) &&
+                     strstr (appinfo->exec,"%U") == NULL &&
+                     strstr (appinfo->exec,"%u") == NULL &&
+                     strstr (appinfo->exec,"%f") == NULL &&
+                     strstr (appinfo->exec,"%F") == NULL)
+                   {
+                     g_object_unref (appinfo);
+                     appinfo = NULL;
+                     hidden = TRUE;
+                   }
+                                     
+                 if (appinfo != NULL || hidden)
+                   {
+                     g_hash_table_insert (apps, g_strdup (desktop_id), appinfo);
+
+                     if (appinfo)
+                       {
+                         /* Reuse instead of strdup here */
+                         appinfo->desktop_id = desktop_id;
+                         desktop_id = NULL;
+                       }
+                   }
+               }
+             g_free (desktop_id);
+           }
+         else
+           {
+             if (g_file_test (filename, G_FILE_TEST_IS_DIR))
+               {
+                 subprefix = g_strconcat (prefix, basename, "-", NULL);
+                 get_apps_from_dir (apps, filename, subprefix);
+                 g_free (subprefix);
+               }
+           }
+         g_free (filename);
+       }
+      g_dir_close (dir);
+    }
+}
+
+static void
+collect_apps (gpointer  key,
+             gpointer  value,
+             gpointer  user_data)
+{
+  GList **infos = user_data;
+
+  if (value)
+    *infos = g_list_prepend (*infos, value);
+}
+
+
+/**
+ * g_app_info_get_all:
+ * 
+ * Returns: a newly allocated #GList of references to #GAppInfo s.
+ **/
+GList *
+g_app_info_get_all (void)
+{
+  const char * const *dirs;
+  GHashTable *apps;
+  int i;
+  GList *infos;
+
+  dirs = get_applications_search_path ();
+
+  apps = g_hash_table_new_full (g_str_hash, g_str_equal,
+                               g_free, NULL);
+
+  
+  for (i = 0; dirs[i] != NULL; i++)
+    get_apps_from_dir (apps, dirs[i], "");
+
+
+  infos = NULL;
+  g_hash_table_foreach (apps,
+                       collect_apps,
+                       &infos);
+
+  g_hash_table_destroy (apps);
+
+  return g_list_reverse (infos);
+}
+
+/* Cacheing of mimeinfo.cache and defaults.list files */
+
+typedef struct {
+  char *path;
+  GHashTable *mime_info_cache_map;
+  GHashTable *defaults_list_map;
+  time_t mime_info_cache_timestamp;
+  time_t defaults_list_timestamp;
+} MimeInfoCacheDir;
+
+typedef struct {
+  GList *dirs;                       /* mimeinfo.cache and defaults.list */
+  GHashTable *global_defaults_cache; /* global results of defaults.list lookup and validation */
+  time_t last_stat_time;
+  guint should_ping_mime_monitor : 1;
+} MimeInfoCache;
+
+static MimeInfoCache *mime_info_cache = NULL;
+G_LOCK_DEFINE_STATIC (mime_info_cache);
+
+static void mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
+                                                    const char *mime_type,
+                                                    char **new_desktop_file_ids);
+
+static MimeInfoCache * mime_info_cache_new (void);
+
+static void
+destroy_info_cache_value (gpointer key, GList *value, gpointer data)
+{
+  g_list_foreach (value, (GFunc)g_free, NULL);
+  g_list_free (value);
+}
+
+static void
+destroy_info_cache_map (GHashTable *info_cache_map)
+{
+  g_hash_table_foreach (info_cache_map, (GHFunc)destroy_info_cache_value, NULL);
+  g_hash_table_destroy (info_cache_map);
+}
+
+static gboolean
+mime_info_cache_dir_out_of_date (MimeInfoCacheDir *dir,
+                                const char *cache_file,
+                                time_t *timestamp)
+{
+  struct stat buf;
+  char *filename;
+  
+  filename = g_build_filename (dir->path, cache_file, NULL);
+  
+  if (g_stat (filename, &buf) < 0)
+    {
+      g_free (filename);
+      return TRUE;
+    }
+  g_free (filename);
+
+  if (buf.st_mtime != *timestamp) 
+    return TRUE;
+  
+  return FALSE;
+}
+
+/* Call with lock held */
+static gboolean
+remove_all (gpointer  key,
+           gpointer  value,
+           gpointer  user_data)
+{
+  return TRUE;
+}
+
+
+static void
+mime_info_cache_blow_global_cache (void)
+{
+  g_hash_table_foreach_remove (mime_info_cache->global_defaults_cache,
+                              remove_all, NULL);
+}
+
+static void
+mime_info_cache_dir_init (MimeInfoCacheDir *dir)
+{
+  GError *load_error;
+  GKeyFile *key_file;
+  gchar *filename, **mime_types;
+  int i;
+  struct stat buf;
+  
+  load_error = NULL;
+  mime_types = NULL;
+  
+  if (dir->mime_info_cache_map != NULL &&
+      !mime_info_cache_dir_out_of_date (dir, "mimeinfo.cache",
+                                       &dir->mime_info_cache_timestamp))
+    return;
+  
+  if (dir->mime_info_cache_map != NULL)
+    destroy_info_cache_map (dir->mime_info_cache_map);
+  
+  dir->mime_info_cache_map = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                   (GDestroyNotify) g_free,
+                                                   NULL);
+  
+  key_file = g_key_file_new ();
+  
+  filename = g_build_filename (dir->path, "mimeinfo.cache", NULL);
+  
+  if (g_stat (filename, &buf) < 0)
+    goto error;
+  
+  if (dir->mime_info_cache_timestamp > 0) 
+    mime_info_cache->should_ping_mime_monitor = TRUE;
+  
+  dir->mime_info_cache_timestamp = buf.st_mtime;
+  
+  g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
+  
+  g_free (filename);
+  filename = NULL;
+  
+  if (load_error != NULL)
+    goto error;
+  
+  mime_types = g_key_file_get_keys (key_file, MIME_CACHE_GROUP,
+                                   NULL, &load_error);
+  
+  if (load_error != NULL)
+    goto error;
+  
+  for (i = 0; mime_types[i] != NULL; i++)
+    {
+      gchar **desktop_file_ids;
+      char *unaliased_type;
+      desktop_file_ids = g_key_file_get_string_list (key_file,
+                                                    MIME_CACHE_GROUP,
+                                                    mime_types[i],
+                                                    NULL,
+                                                    NULL);
+      
+      if (desktop_file_ids == NULL)
+       continue;
+
+      unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
+      mime_info_cache_dir_add_desktop_entries (dir,
+                                              unaliased_type,
+                                              desktop_file_ids);
+      g_free (unaliased_type);
+    
+      g_strfreev (desktop_file_ids);
+    }
+  
+  g_strfreev (mime_types);
+  g_key_file_free (key_file);
+  
+  return;
+ error:
+  g_free (filename);
+  g_key_file_free (key_file);
+  
+  if (mime_types != NULL)
+    g_strfreev (mime_types);
+  
+  if (load_error)
+    g_error_free (load_error);
+}
+
+static void
+mime_info_cache_dir_init_defaults_list (MimeInfoCacheDir *dir)
+{
+  GKeyFile *key_file;
+  GError *load_error;
+  gchar *filename, **mime_types;
+  char *unaliased_type;
+  char **desktop_file_ids;
+  int i;
+  struct stat buf;
+
+  load_error = NULL;
+  mime_types = NULL;
+
+  if (dir->defaults_list_map != NULL &&
+      !mime_info_cache_dir_out_of_date (dir, "defaults.list",
+                                       &dir->defaults_list_timestamp))
+    return;
+  
+  if (dir->defaults_list_map != NULL)
+    g_hash_table_destroy (dir->defaults_list_map);
+
+  dir->defaults_list_map = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                 g_free, (GDestroyNotify)g_strfreev);
+
+  key_file = g_key_file_new ();
+  
+  filename = g_build_filename (dir->path, "defaults.list", NULL);
+  if (g_stat (filename, &buf) < 0)
+    goto error;
+
+  if (dir->defaults_list_timestamp > 0) 
+    mime_info_cache->should_ping_mime_monitor = TRUE;
+
+  dir->defaults_list_timestamp = buf.st_mtime;
+
+  g_key_file_load_from_file (key_file, filename, G_KEY_FILE_NONE, &load_error);
+  g_free (filename);
+  filename = NULL;
+
+  if (load_error != NULL)
+    goto error;
+
+  mime_types = g_key_file_get_keys (key_file, DEFAULT_APPLICATIONS_GROUP,
+                                   NULL, &load_error);
+
+  if (load_error != NULL)
+    goto error;
+
+  for (i = 0; mime_types[i] != NULL; i++)
+    {
+      desktop_file_ids = g_key_file_get_string_list (key_file,
+                                                    DEFAULT_APPLICATIONS_GROUP,
+                                                    mime_types[i],
+                                                    NULL,
+                                                    NULL);
+      if (desktop_file_ids == NULL)
+       continue;
+
+      unaliased_type = _g_unix_content_type_unalias (mime_types[i]);
+      g_hash_table_replace (dir->defaults_list_map,
+                           unaliased_type,
+                           desktop_file_ids);
+    }
+
+  g_strfreev (mime_types);
+  g_key_file_free (key_file);
+  
+  return;
+ error:
+  g_free (filename);
+  g_key_file_free (key_file);
+  
+  if (mime_types != NULL)
+    g_strfreev (mime_types);
+  
+  if (load_error)
+    g_error_free (load_error);
+}
+
+static MimeInfoCacheDir *
+mime_info_cache_dir_new (const char *path)
+{
+  MimeInfoCacheDir *dir;
+
+  dir = g_new0 (MimeInfoCacheDir, 1);
+  dir->path = g_strdup (path);
+  
+  return dir;
+}
+
+static void
+mime_info_cache_dir_free (MimeInfoCacheDir *dir)
+{
+  if (dir == NULL)
+    return;
+  
+  if (dir->mime_info_cache_map != NULL)
+    {
+      destroy_info_cache_map (dir->mime_info_cache_map);
+      dir->mime_info_cache_map = NULL;
+      
+  }
+  
+  if (dir->defaults_list_map != NULL)
+    {
+      g_hash_table_destroy (dir->defaults_list_map);
+      dir->defaults_list_map = NULL;
+    }
+  
+  g_free (dir);
+}
+
+static void
+mime_info_cache_dir_add_desktop_entries (MimeInfoCacheDir *dir,
+                                        const char *mime_type,
+                                        char **new_desktop_file_ids)
+{
+  GList *desktop_file_ids;
+  int i;
+  
+  desktop_file_ids = g_hash_table_lookup (dir->mime_info_cache_map,
+                                         mime_type);
+  
+  for (i = 0; new_desktop_file_ids[i] != NULL; i++)
+    {
+      if (!g_list_find (desktop_file_ids, new_desktop_file_ids[i]))
+       desktop_file_ids = g_list_append (desktop_file_ids,
+                                         g_strdup (new_desktop_file_ids[i]));
+    }
+  
+  g_hash_table_insert (dir->mime_info_cache_map, g_strdup (mime_type), desktop_file_ids);
+}
+
+static void
+mime_info_cache_init_dir_lists (void)
+{
+  const char * const *dirs;
+  int i;
+  
+  mime_info_cache = mime_info_cache_new ();
+  
+  dirs = get_applications_search_path ();
+  
+  for (i = 0; dirs[i] != NULL; i++)
+    {
+      MimeInfoCacheDir *dir;
+      
+      dir = mime_info_cache_dir_new (dirs[i]);
+      
+      if (dir != NULL)
+       {
+         mime_info_cache_dir_init (dir);
+         mime_info_cache_dir_init_defaults_list (dir);
+         
+         mime_info_cache->dirs = g_list_append (mime_info_cache->dirs, dir);
+       }
+    }
+}
+
+static void
+mime_info_cache_update_dir_lists (void)
+{
+  GList *tmp;
+  
+  tmp = mime_info_cache->dirs;
+  
+  while (tmp != NULL)
+    {
+      MimeInfoCacheDir *dir = (MimeInfoCacheDir *) tmp->data;
+
+      /* No need to do this if we had file monitors... */
+      mime_info_cache_blow_global_cache ();
+      mime_info_cache_dir_init (dir);
+      mime_info_cache_dir_init_defaults_list (dir);
+      
+      tmp = tmp->next;
+    }
+}
+
+static void
+mime_info_cache_init (void)
+{
+       G_LOCK (mime_info_cache);
+       if (mime_info_cache == NULL)
+         mime_info_cache_init_dir_lists ();
+       else
+         {
+           time_t now;
+           
+           time (&now);
+           if (now >= mime_info_cache->last_stat_time + 10)
+             {
+               mime_info_cache_update_dir_lists ();
+               mime_info_cache->last_stat_time = now;
+             }
+         }
+
+       if (mime_info_cache->should_ping_mime_monitor)
+         {
+           /* g_idle_add (emit_mime_changed, NULL); */
+           mime_info_cache->should_ping_mime_monitor = FALSE;
+         }
+       
+       G_UNLOCK (mime_info_cache);
+}
+
+static MimeInfoCache *
+mime_info_cache_new (void)
+{
+  MimeInfoCache *cache;
+  
+  cache = g_new0 (MimeInfoCache, 1);
+  
+  cache->global_defaults_cache = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                       (GDestroyNotify) g_free,
+                                                       (GDestroyNotify) g_free);
+  return cache;
+}
+
+static void
+mime_info_cache_free (MimeInfoCache *cache)
+{
+  if (cache == NULL)
+    return;
+  
+  g_list_foreach (cache->dirs,
+                 (GFunc) mime_info_cache_dir_free,
+                 NULL);
+  g_list_free (cache->dirs);
+  g_hash_table_destroy (cache->global_defaults_cache);
+  g_free (cache);
+}
+
+/**
+ * mime_info_cache_reload:
+ * @dir: directory path which needs reloading.
+ * 
+ * Reload the mime information for the @dir.
+ */
+static void
+mime_info_cache_reload (const char *dir)
+{
+  /* FIXME: just reload the dir that needs reloading,
+   * don't blow the whole cache
+   */
+  if (mime_info_cache != NULL)
+    {
+      G_LOCK (mime_info_cache);
+      mime_info_cache_free (mime_info_cache);
+      mime_info_cache = NULL;
+      G_UNLOCK (mime_info_cache);
+    }
+}
+
+static GList *
+append_desktop_entry (GList *list, const char *desktop_entry)
+{
+  /* Add if not already in list, and valid */
+  if (!g_list_find_custom (list, desktop_entry, (GCompareFunc) strcmp))
+    list = g_list_prepend (list, g_strdup (desktop_entry));
+  
+  return list;
+}
+
+/**
+ * get_all_desktop_entries_for_mime_type:
+ * @mime_type: a mime type.
+ *
+ * Returns all the desktop filenames for @mime_type. The desktop files
+ * are listed in an order so that default applications are listed before
+ * non-default ones, and handlers for inherited mimetypes are listed
+ * after the base ones.
+ *
+ * Return value: a #GList containing the desktop filenames containing the
+ * @mime_type.
+ */
+static GList *
+get_all_desktop_entries_for_mime_type (const char *base_mime_type)
+{
+  GList *desktop_entries, *list, *dir_list, *tmp;
+  MimeInfoCacheDir *dir;
+  char *mime_type;
+  char **mime_types;
+  char **default_entries;
+  int i,j;
+  
+  mime_info_cache_init ();
+
+  mime_types = _g_unix_content_type_get_parents (base_mime_type);
+  G_LOCK (mime_info_cache);
+  
+  desktop_entries = NULL;
+  for (i = 0; mime_types[i] != NULL; i++)
+    {
+      mime_type = mime_types[i];
+
+      /* Go through all apps listed as defaults */
+      for (dir_list = mime_info_cache->dirs;
+          dir_list != NULL;
+          dir_list = dir_list->next)
+       {
+         dir = dir_list->data;
+         default_entries = g_hash_table_lookup (dir->defaults_list_map, mime_type);
+         for (j = 0; default_entries != NULL && default_entries[j] != NULL; j++)
+           desktop_entries = append_desktop_entry (desktop_entries, default_entries[j]);
+       }
+
+      /* Go through all entries that support the mimetype */
+      for (dir_list = mime_info_cache->dirs;
+          dir_list != NULL;
+          dir_list = dir_list->next) {
+       dir = dir_list->data;
+       
+       list = g_hash_table_lookup (dir->mime_info_cache_map, mime_type);
+       for (tmp = list; tmp != NULL; tmp = tmp->next) {
+         desktop_entries = append_desktop_entry (desktop_entries, tmp->data);
+       }
+      }
+    }
+  
+  G_UNLOCK (mime_info_cache);
+
+  g_strfreev (mime_types);
+  
+  desktop_entries = g_list_reverse (desktop_entries);
+  
+  return desktop_entries;
+}
diff --git a/gio/gdesktopappinfo.h b/gio/gdesktopappinfo.h
new file mode 100644 (file)
index 0000000..0f667dc
--- /dev/null
@@ -0,0 +1,54 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DESKTOP_APP_INFO_H__
+#define __G_DESKTOP_APP_INFO_H__
+
+#include <gio/gappinfo.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DESKTOP_APP_INFO         (g_desktop_app_info_get_type ())
+#define G_DESKTOP_APP_INFO(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DESKTOP_APP_INFO, GDesktopAppInfo))
+#define G_DESKTOP_APP_INFO_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DESKTOP_APP_INFO, GDesktopAppInfoClass))
+#define G_IS_DESKTOP_APP_INFO(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DESKTOP_APP_INFO))
+#define G_IS_DESKTOP_APP_INFO_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DESKTOP_APP_INFO))
+#define G_DESKTOP_APP_INFO_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DESKTOP_APP_INFO, GDesktopAppInfoClass))
+
+typedef struct _GDesktopAppInfo        GDesktopAppInfo;
+typedef struct _GDesktopAppInfoClass   GDesktopAppInfoClass;
+
+struct _GDesktopAppInfoClass
+{
+  GObjectClass parent_class;
+};
+
+GType g_desktop_app_info_get_type (void) G_GNUC_CONST;
+  
+GDesktopAppInfo *g_desktop_app_info_new_from_filename (const char      *filename);
+GDesktopAppInfo *g_desktop_app_info_new               (const char      *desktop_id);
+gboolean         g_desktop_app_info_get_is_hidden     (GDesktopAppInfo *info);
+
+G_END_DECLS
+
+
+#endif /* __G_DESKTOP_APP_INFO_H__ */
diff --git a/gio/gdirectorymonitor.c b/gio/gdirectorymonitor.c
new file mode 100644 (file)
index 0000000..eb50a95
--- /dev/null
@@ -0,0 +1,472 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "gdirectorymonitor.h"
+#include "gio-marshal.h"
+#include "gfile.h"
+#include "gvfs.h"
+#include "glibintl.h"
+
+enum {
+  CHANGED,
+  LAST_SIGNAL
+};
+
+G_DEFINE_ABSTRACT_TYPE (GDirectoryMonitor, g_directory_monitor, G_TYPE_OBJECT);
+
+typedef struct {
+  GFile *file;
+  guint32 last_sent_change_time; /* 0 == not sent */
+  guint32 send_delayed_change_at; /* 0 == never */
+  guint32 send_virtual_changes_done_at; /* 0 == never */
+} RateLimiter;
+
+struct _GDirectoryMonitorPrivate {
+  gboolean cancelled;
+  int rate_limit_msec;
+
+  GHashTable *rate_limiter;
+  
+  GSource *timeout;
+  guint32 timeout_fires_at;
+};
+
+#define DEFAULT_RATE_LIMIT_MSECS 800
+#define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+rate_limiter_free (RateLimiter *limiter)
+{
+  g_object_unref (limiter->file);
+  g_free (limiter);
+}
+
+static void
+g_directory_monitor_finalize (GObject *object)
+{
+  GDirectoryMonitor *monitor;
+
+  monitor = G_DIRECTORY_MONITOR (object);
+
+  if (monitor->priv->timeout)
+    {
+      g_source_destroy (monitor->priv->timeout);
+      g_source_unref (monitor->priv->timeout);
+    }
+
+  g_hash_table_destroy (monitor->priv->rate_limiter);
+  
+  if (G_OBJECT_CLASS (g_directory_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_directory_monitor_parent_class)->finalize) (object);
+}
+
+static void
+g_directory_monitor_dispose (GObject *object)
+{
+  GDirectoryMonitor *monitor;
+  
+  monitor = G_DIRECTORY_MONITOR (object);
+
+  /* Make sure we cancel on last unref */
+  if (!monitor->priv->cancelled)
+    g_directory_monitor_cancel (monitor);
+  
+  if (G_OBJECT_CLASS (g_directory_monitor_parent_class)->dispose)
+    (*G_OBJECT_CLASS (g_directory_monitor_parent_class)->dispose) (object);
+}
+
+static void
+g_directory_monitor_class_init (GDirectoryMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GDirectoryMonitorPrivate));
+  
+  gobject_class->finalize = g_directory_monitor_finalize;
+  gobject_class->dispose = g_directory_monitor_dispose;
+
+  /**
+   * GDirectoryMonitor::changed:
+   * @monitor: the #GDirectoryMonitor
+   * @child: the #GFile which changed
+   * @other_file: the other #GFile which changed
+   * @event_type: a #GFileMonitorEvent indicating what the event was
+   *
+   * Emitted when a child file changes.
+   */
+  signals[CHANGED] =
+    g_signal_new (I_("changed"),
+                 G_TYPE_DIRECTORY_MONITOR,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GDirectoryMonitorClass, changed),
+                 NULL, NULL,
+                 _gio_marshal_VOID__OBJECT_OBJECT_INT,
+                 G_TYPE_NONE,3,
+                 G_TYPE_FILE,
+                 G_TYPE_FILE,
+                 G_TYPE_INT);
+}
+
+static void
+g_directory_monitor_init (GDirectoryMonitor *monitor)
+{
+  monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
+                                              G_TYPE_DIRECTORY_MONITOR,
+                                              GDirectoryMonitorPrivate);
+
+  monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
+  monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
+                                                      NULL, (GDestroyNotify) rate_limiter_free);
+}
+
+
+/**
+ * g_directory_monitor_cancel:
+ * @monitor:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_directory_monitor_cancel (GDirectoryMonitor* monitor)
+{
+  GDirectoryMonitorClass *class;
+
+  g_return_val_if_fail (G_IS_DIRECTORY_MONITOR (monitor), FALSE);
+  
+  if (monitor->priv->cancelled)
+    return TRUE;
+  
+  monitor->priv->cancelled = TRUE;
+  
+  class = G_DIRECTORY_MONITOR_GET_CLASS (monitor);
+  return (* class->cancel) (monitor);
+}
+
+/**
+ * g_directory_monitor_set_rate_limit:
+ * @monitor:
+ * @limit_msecs:
+ * 
+ **/
+void
+g_directory_monitor_set_rate_limit (GDirectoryMonitor *monitor,
+                                   int limit_msecs)
+{
+  g_return_if_fail (G_IS_DIRECTORY_MONITOR (monitor));
+
+  monitor->priv->rate_limit_msec = limit_msecs;
+}
+
+/**
+ * g_directory_monitor_is_cancelled:
+ * @monitor:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_directory_monitor_is_cancelled (GDirectoryMonitor *monitor)
+{
+  g_return_val_if_fail (G_IS_DIRECTORY_MONITOR (monitor), FALSE);
+
+  return monitor->priv->cancelled;
+}
+
+static guint32
+get_time_msecs (void)
+{
+  return g_thread_gettime() / (1000 * 1000);
+}
+
+static guint32
+time_difference (guint32 from, guint32 to)
+{
+  if (from > to)
+    return 0;
+  return to - from;
+}
+
+static RateLimiter *
+new_limiter (GDirectoryMonitor *monitor,
+            GFile *file)
+{
+  RateLimiter *limiter;
+
+  limiter = g_new0 (RateLimiter, 1);
+  limiter->file = g_object_ref (file);
+  g_hash_table_insert (monitor->priv->rate_limiter, file, limiter);
+  
+  return limiter;
+}
+
+static void
+rate_limiter_send_virtual_changes_done_now (GDirectoryMonitor *monitor, RateLimiter *limiter)
+{
+  if (limiter->send_virtual_changes_done_at != 0)
+    {
+      g_signal_emit (monitor, signals[CHANGED], 0, limiter->file, NULL, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
+      limiter->send_virtual_changes_done_at = 0;
+    }
+}
+
+static void
+rate_limiter_send_delayed_change_now (GDirectoryMonitor *monitor, RateLimiter *limiter, guint32 time_now)
+{
+  if (limiter->send_delayed_change_at != 0)
+    {
+      g_signal_emit (monitor, signals[CHANGED], 0, limiter->file, NULL, G_FILE_MONITOR_EVENT_CHANGED);
+      limiter->send_delayed_change_at = 0;
+      limiter->last_sent_change_time = time_now;
+    }
+}
+
+typedef struct {
+  guint32 min_time;
+  guint32 time_now;
+  GDirectoryMonitor *monitor;
+} ForEachData;
+
+static gboolean
+calc_min_time (GDirectoryMonitor *monitor, RateLimiter *limiter, guint32 time_now, guint32 *min_time)
+{
+  gboolean delete_me;
+  guint32 expire_at;
+
+  delete_me = TRUE;
+
+  if (limiter->last_sent_change_time != 0)
+    {
+      /* Set a timeout at 2*rate limit so that we can clear out the change from the hash eventualy */
+      expire_at = limiter->last_sent_change_time + 2 * monitor->priv->rate_limit_msec;
+
+      if (time_difference (time_now, expire_at) > 0)
+       {
+         delete_me = FALSE;
+         *min_time = MIN (*min_time,
+                          time_difference (time_now, expire_at));
+       }
+    }
+
+  if (limiter->send_delayed_change_at != 0)
+    {
+      delete_me = FALSE;
+      *min_time = MIN (*min_time,
+                      time_difference (time_now, limiter->send_delayed_change_at));
+    }
+
+  if (limiter->send_virtual_changes_done_at != 0)
+    {
+      delete_me = FALSE;
+      *min_time = MIN (*min_time,
+                      time_difference (time_now, limiter->send_virtual_changes_done_at));
+    }
+
+  return delete_me;
+}
+
+static gboolean
+foreach_rate_limiter_fire (gpointer  key,
+                          gpointer  value,
+                          gpointer  user_data)
+{
+  RateLimiter *limiter = value;
+  ForEachData *data = user_data;
+
+  if (limiter->send_delayed_change_at != 0 &&
+      time_difference (data->time_now, limiter->send_delayed_change_at) == 0)
+    rate_limiter_send_delayed_change_now (data->monitor, limiter, data->time_now);
+
+  if (limiter->send_virtual_changes_done_at != 0 &&
+      time_difference (data->time_now, limiter->send_virtual_changes_done_at) == 0)
+    rate_limiter_send_virtual_changes_done_now (data->monitor, limiter);
+
+  return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
+}
+
+static gboolean 
+rate_limiter_timeout (gpointer timeout_data)
+{
+  GDirectoryMonitor *monitor = timeout_data;
+  ForEachData data;
+  GSource *source;
+  
+  data.min_time = G_MAXUINT32;
+  data.monitor = monitor;
+  data.time_now = get_time_msecs ();
+  g_hash_table_foreach_remove (monitor->priv->rate_limiter,
+                              foreach_rate_limiter_fire,
+                              &data);
+
+  /* Remove old timeout */
+  if (monitor->priv->timeout)
+    {
+      g_source_destroy (monitor->priv->timeout);
+      g_source_unref (monitor->priv->timeout);
+      monitor->priv->timeout = NULL;
+      monitor->priv->timeout_fires_at = 0;
+    }
+  
+  /* Set up new timeout */
+  if (data.min_time != G_MAXUINT32)
+    {
+      source = g_timeout_source_new (data.min_time + 1); /* + 1 to make sure we've really passed the time */
+      g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
+      g_source_attach (source, NULL);
+      
+      monitor->priv->timeout = source;
+      monitor->priv->timeout_fires_at = data.time_now + data.min_time; 
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+foreach_rate_limiter_update (gpointer  key,
+                            gpointer  value,
+                            gpointer  user_data)
+{
+  RateLimiter *limiter = value;
+  ForEachData *data = user_data;
+
+  return calc_min_time (data->monitor, limiter, data->time_now, &data->min_time);
+}
+
+static void
+update_rate_limiter_timeout (GDirectoryMonitor *monitor, guint new_time)
+{
+  ForEachData data;
+  GSource *source;
+  
+  if (monitor->priv->timeout_fires_at != 0 && new_time != 0 &&
+      time_difference (new_time, monitor->priv->timeout_fires_at) == 0)
+    return; /* Nothing to do, we already fire earlier than that */
+
+  data.min_time = G_MAXUINT32;
+  data.monitor = monitor;
+  data.time_now = get_time_msecs ();
+  g_hash_table_foreach_remove (monitor->priv->rate_limiter,
+                              foreach_rate_limiter_update,
+                              &data);
+
+  /* Remove old timeout */
+  if (monitor->priv->timeout)
+    {
+      g_source_destroy (monitor->priv->timeout);
+      g_source_unref (monitor->priv->timeout);
+      monitor->priv->timeout_fires_at = 0;
+      monitor->priv->timeout = NULL;
+    }
+
+  /* Set up new timeout */
+  if (data.min_time != G_MAXUINT32)
+    {
+      source = g_timeout_source_new (data.min_time + 1);  /* + 1 to make sure we've really passed the time */
+      g_source_set_callback (source, rate_limiter_timeout, monitor, NULL);
+      g_source_attach (source, NULL);
+      
+      monitor->priv->timeout = source;
+      monitor->priv->timeout_fires_at = data.time_now + data.min_time; 
+    }
+}
+
+/**
+ * g_directory_monitor_emit_event:
+ * @monitor:
+ * @child:
+ * @other_file:
+ * @event_type:
+ * 
+ **/
+void
+g_directory_monitor_emit_event (GDirectoryMonitor *monitor,
+                               GFile *child,
+                               GFile *other_file,
+                               GFileMonitorEvent event_type)
+{
+  guint32 time_now, since_last;
+  gboolean emit_now;
+  RateLimiter *limiter;
+
+  g_return_if_fail (G_IS_DIRECTORY_MONITOR (monitor));
+  g_return_if_fail (G_IS_FILE (child));
+
+  limiter = g_hash_table_lookup (monitor->priv->rate_limiter, child);
+
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
+    {
+      if (limiter)
+       {
+         rate_limiter_send_delayed_change_now (monitor, limiter, get_time_msecs ());
+         if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+           limiter->send_virtual_changes_done_at = 0;
+         else
+           rate_limiter_send_virtual_changes_done_now (monitor, limiter);
+         update_rate_limiter_timeout (monitor, 0);
+       }
+      g_signal_emit (monitor, signals[CHANGED], 0, child, other_file, event_type);
+    }
+  else
+    {
+      /* Changed event, rate limit */
+      time_now = get_time_msecs ();
+      emit_now = TRUE;
+      
+      if (limiter)
+       {
+         since_last = time_difference (limiter->last_sent_change_time, time_now);
+         if (since_last < monitor->priv->rate_limit_msec)
+           {
+             /* We ignore this change, but arm a timer so that we can fire it later if we
+                don't get any other events (that kill this timeout) */
+             emit_now = FALSE;
+             if (limiter->send_delayed_change_at == 0)
+               {
+                 limiter->send_delayed_change_at = time_now + monitor->priv->rate_limit_msec;
+                 update_rate_limiter_timeout (monitor, limiter->send_delayed_change_at);
+               }
+           }
+       }
+
+      if (limiter == NULL)
+       limiter = new_limiter (monitor, child);
+      
+      if (emit_now)
+       {
+         g_signal_emit (monitor, signals[CHANGED], 0, child, other_file, event_type);
+
+         limiter->last_sent_change_time = time_now;
+         limiter->send_delayed_change_at = 0;
+         /* Set a timeout of 2*rate limit so that we can clear out the change from the hash eventualy */
+         update_rate_limiter_timeout (monitor, time_now + 2 * monitor->priv->rate_limit_msec);
+       }
+
+      /* Schedule a virtual change done. This is removed if we get a real one, and
+        postponed if we get more change events. */
+
+      limiter->send_virtual_changes_done_at = time_now + DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS * 1000;
+      update_rate_limiter_timeout (monitor, limiter->send_virtual_changes_done_at);
+    }
+}
diff --git a/gio/gdirectorymonitor.h b/gio/gdirectorymonitor.h
new file mode 100644 (file)
index 0000000..d4e3573
--- /dev/null
@@ -0,0 +1,86 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DIRECTORY_MONITOR_H__
+#define __G_DIRECTORY_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gfile.h>
+#include <gio/gfilemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DIRECTORY_MONITOR         (g_directory_monitor_get_type ())
+#define G_DIRECTORY_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DIRECTORY_MONITOR, GDirectoryMonitor))
+#define G_DIRECTORY_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DIRECTORY_MONITOR, GDirectoryMonitorClass))
+#define G_IS_DIRECTORY_MONITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DIRECTORY_MONITOR))
+#define G_IS_DIRECTORY_MONITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DIRECTORY_MONITOR))
+#define G_DIRECTORY_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DIRECTORY_MONITOR, GDirectoryMonitorClass))
+
+typedef struct _GDirectoryMonitorClass  GDirectoryMonitorClass;
+typedef struct _GDirectoryMonitorPrivate GDirectoryMonitorPrivate;
+
+struct _GDirectoryMonitor
+{
+  GObject parent;
+
+  /*< private >*/
+  GDirectoryMonitorPrivate *priv;
+};
+
+struct _GDirectoryMonitorClass
+{
+  GObjectClass parent_class;
+  
+  /* Signals */
+  void (* changed) (GDirectoryMonitor* monitor,
+                   GFile *child,
+                   GFile *other_file,
+                   GFileMonitorEvent event_type);
+  
+  /* Virtual Table */
+  gboolean     (*cancel)(GDirectoryMonitor* monitor);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_directory_monitor_get_type (void) G_GNUC_CONST;
+
+gboolean g_directory_monitor_cancel         (GDirectoryMonitor *monitor);
+gboolean g_directory_monitor_is_cancelled   (GDirectoryMonitor *monitor);
+void     g_directory_monitor_set_rate_limit (GDirectoryMonitor *monitor,
+                                            int                limit_msecs);
+
+/* For implementations */
+void g_directory_monitor_emit_event (GDirectoryMonitor      *monitor,
+                                    GFile                  *child,
+                                    GFile                  *other_file,
+                                    GFileMonitorEvent       event_type);
+
+G_END_DECLS
+
+#endif /* __G_DIRECTORY_MONITOR_H__ */
diff --git a/gio/gdrive.c b/gio/gdrive.c
new file mode 100644 (file)
index 0000000..b953c2c
--- /dev/null
@@ -0,0 +1,348 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gdrive.h"
+#include "gsimpleasyncresult.h"
+#include "glibintl.h"
+
+static void g_drive_base_init (gpointer g_class);
+static void g_drive_class_init (gpointer g_class,
+                                gpointer class_data);
+
+GType
+g_drive_get_type (void)
+{
+  static GType drive_type = 0;
+
+  if (! drive_type)
+    {
+      static const GTypeInfo drive_info =
+      {
+        sizeof (GDriveIface), /* class_size */
+       g_drive_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       g_drive_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      drive_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GDrive"),
+                               &drive_info, 0);
+
+      g_type_interface_add_prerequisite (drive_type, G_TYPE_OBJECT);
+    }
+
+  return drive_type;
+}
+
+static void
+g_drive_class_init (gpointer g_class,
+                  gpointer class_data)
+{
+}
+
+static void
+g_drive_base_init (gpointer g_class)
+{
+  static gboolean initialized = FALSE;
+
+  if (! initialized)
+    {
+      g_signal_new (I_("changed"),
+                    G_TYPE_DRIVE,
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GDriveIface, changed),
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__VOID,
+                    G_TYPE_NONE, 0);
+
+      initialized = TRUE;
+    }
+}
+
+/**
+ * g_drive_get_name:
+ * @drive: a #GDrive.
+ * 
+ * Returns: string containing @drive's name.
+ *
+ * The returned string should be freed when no longer needed
+ **/
+char *
+g_drive_get_name (GDrive *drive)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  return (* iface->get_name) (drive);
+}
+
+/**
+ * g_drive_get_icon:
+ * @drive: a #GDrive.
+ * 
+ * Gets the icon for @drive.
+ * 
+ * Returns: #GIcon for the @drive.
+ **/
+GIcon *
+g_drive_get_icon (GDrive *drive)
+{
+  GDriveIface *iface;
+  
+  g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  return (* iface->get_icon) (drive);
+}
+
+/**
+ * g_drive_has_volumes:
+ * @drive: a #GDrive.
+ * 
+ * Returns: %TRUE if @drive contains volumes, %FALSE otherwise.
+ **/
+gboolean
+g_drive_has_volumes (GDrive *drive)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  return (* iface->has_volumes) (drive);
+}
+
+/**
+ * g_drive_get_volumes:
+ * @drive: a #GDrive.
+ * 
+ * Returns: #GList containing any #GVolume s on the given @drive.
+ * NOTE: Fact-check this.
+ **/
+GList *
+g_drive_get_volumes (GDrive *drive)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), NULL);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  return (* iface->get_volumes) (drive);
+}
+
+/**
+ * g_drive_is_automounted:
+ * @drive: a #GDrive.
+ * 
+ * Returns: %TRUE if the drive was automounted. %FALSE otherwise.
+ **/
+gboolean
+g_drive_is_automounted (GDrive *drive)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  return (* iface->is_automounted) (drive);
+}
+
+/**
+ * g_drive_can_mount:
+ * @drive: a #GDrive.
+ * 
+ * Returns: %TRUE if the @drive can be mounted. %FALSE otherwise.
+ **/
+gboolean
+g_drive_can_mount (GDrive *drive)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  if (iface->can_mount == NULL)
+    return FALSE;
+
+  return (* iface->can_mount) (drive);
+}
+
+/**
+ * g_drive_can_eject:
+ * @drive: pointer to a #GDrive.
+ * 
+ * Returns: %TRUE if the @drive can be ejected. %FALSE otherwise.
+ **/
+gboolean
+g_drive_can_eject (GDrive *drive)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  if (iface->can_eject == NULL)
+    return FALSE;
+
+  return (* iface->can_eject) (drive);
+}
+
+/**
+ * g_drive_mount:
+ * @drive: a #GDrive.
+ * @mount_operation: a #GMountOperation.
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: a #gpointer.
+ * 
+ * 
+ **/
+void
+g_drive_mount (GDrive         *drive,
+              GMountOperation *mount_operation,
+              GCancellable *cancellable,
+              GAsyncReadyCallback callback,
+              gpointer         user_data)
+{
+  GDriveIface *iface;
+
+  g_return_if_fail (G_IS_DRIVE (drive));
+  g_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  if (iface->mount == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (drive), callback, user_data,
+                                          G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                          _("drive doesn't implement mount"));
+      
+      return;
+    }
+  
+  (* iface->mount) (drive, mount_operation, cancellable, callback, user_data);
+}
+
+/**
+ * g_drive_mount_finish:
+ * @drive: pointer to a #GDrive.
+ * @result: a #GAsyncResult.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE, %FALSE if operation failed.
+ **/
+gboolean
+g_drive_mount_finish (GDrive               *drive,
+                     GAsyncResult         *result,
+                     GError              **error)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_DRIVE_GET_IFACE (drive);
+  return (* iface->mount_finish) (drive, result, error);
+}
+
+/**
+ * g_drive_eject:
+ * @drive: a #GDrive.
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: a #gpointer.
+ * 
+ **/
+void
+g_drive_eject (GDrive         *drive,
+              GCancellable *cancellable,
+              GAsyncReadyCallback  callback,
+              gpointer         user_data)
+{
+  GDriveIface *iface;
+
+  g_return_if_fail (G_IS_DRIVE (drive));
+
+  iface = G_DRIVE_GET_IFACE (drive);
+
+  if (iface->eject == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (drive), callback, user_data,
+                                          G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                          _("drive doesn't implement eject"));
+      
+      return;
+    }
+  
+  (* iface->eject) (drive, cancellable, callback, user_data);
+}
+
+/**
+ * g_drive_eject_finish
+ * @drive: a #GDrive.
+ * @result: a #GAsyncResult.
+ * @error: a #GError.
+ * 
+ * Returns: %TRUE if the drive has been ejected successfully,
+ * %FALSE otherwise.
+ **/
+gboolean
+g_drive_eject_finish (GDrive               *drive,
+                     GAsyncResult         *result,
+                     GError              **error)
+{
+  GDriveIface *iface;
+
+  g_return_val_if_fail (G_IS_DRIVE (drive), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_DRIVE_GET_IFACE (drive);
+  
+  return (* iface->mount_finish) (drive, result, error);
+}
diff --git a/gio/gdrive.h b/gio/gdrive.h
new file mode 100644 (file)
index 0000000..7a0413e
--- /dev/null
@@ -0,0 +1,99 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DRIVE_H__
+#define __G_DRIVE_H__
+
+#include <glib-object.h>
+#include <gio/gvolume.h>
+#include <gio/gmountoperation.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DRIVE           (g_drive_get_type ())
+#define G_DRIVE(obj)           (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_DRIVE, GDrive))
+#define G_IS_DRIVE(obj)               (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_DRIVE))
+#define G_DRIVE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_DRIVE, GDriveIface))
+
+typedef struct _GDriveIface    GDriveIface;
+
+struct _GDriveIface
+{
+  GTypeInterface g_iface;
+
+  /* signals */
+  void (*changed)            (GVolume *volume);
+  
+  /* Virtual Table */
+  
+  char *   (*get_name)    (GDrive         *drive);
+  GIcon *  (*get_icon)    (GDrive         *drive);
+  gboolean (*has_volumes) (GDrive         *drive);
+  GList *  (*get_volumes) (GDrive         *drive);
+  gboolean (*is_automounted)(GDrive       *drive);
+  gboolean (*can_mount)   (GDrive         *drive);
+  gboolean (*can_eject)   (GDrive         *drive);
+  void     (*mount)       (GDrive         *drive,
+                          GMountOperation *mount_operation,
+                          GCancellable   *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer        user_data);
+  gboolean (*mount_finish)(GDrive         *drive,
+                          GAsyncResult   *result,
+                          GError        **error);
+  void     (*eject)       (GDrive         *drive,
+                          GCancellable   *cancellable,
+                          GAsyncReadyCallback callback,
+                          gpointer        user_data);
+  gboolean (*eject_finish)(GDrive         *drive,
+                          GAsyncResult   *result,
+                          GError        **error);
+};
+
+GType g_drive_get_type (void) G_GNUC_CONST;
+
+char *   g_drive_get_name       (GDrive               *drive);
+GIcon *  g_drive_get_icon       (GDrive               *drive);
+gboolean g_drive_has_volumes    (GDrive               *drive);
+GList  * g_drive_get_volumes    (GDrive               *drive);
+gboolean g_drive_is_automounted (GDrive               *drive);
+gboolean g_drive_can_mount      (GDrive               *drive);
+gboolean g_drive_can_eject      (GDrive               *drive);
+void     g_drive_mount          (GDrive               *drive,
+                                GMountOperation      *mount_operation,
+                                GCancellable         *cancellable,
+                                GAsyncReadyCallback   callback,
+                                gpointer              user_data);
+gboolean g_drive_mount_finish   (GDrive               *drive,
+                                GAsyncResult         *result,
+                                GError              **error);
+void     g_drive_eject          (GDrive               *drive,
+                                GCancellable         *cancellable,
+                                GAsyncReadyCallback   callback,
+                                gpointer              user_data);
+gboolean g_drive_eject_finish   (GDrive               *drive,
+                                GAsyncResult         *result,
+                                GError              **error);
+
+G_END_DECLS
+
+#endif /* __G_DRIVE_H__ */
diff --git a/gio/gdriveprivate.h b/gio/gdriveprivate.h
new file mode 100644 (file)
index 0000000..bd53b2a
--- /dev/null
@@ -0,0 +1,32 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DRIVEPRIV_H__
+#define __G_DRIVEPRIV_H__
+
+#include <gio/gdrive.h>
+
+G_BEGIN_DECLS
+
+G_END_DECLS
+
+#endif /* __G_DRIVEPRIV_H__ */
diff --git a/gio/gdummyfile.c b/gio/gdummyfile.c
new file mode 100644 (file)
index 0000000..40ac3b1
--- /dev/null
@@ -0,0 +1,752 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "gdummyfile.h"
+
+static void g_dummy_file_file_iface_init (GFileIface       *iface);
+
+typedef struct {
+  char *scheme;
+  char *userinfo;
+  char *host;
+  int port; /* -1 => not in uri */
+  char *path;
+  char *query;
+  char *fragment;
+} GDecodedUri;
+
+struct _GDummyFile
+{
+  GObject parent_instance;
+
+  GDecodedUri *decoded_uri;
+  char *text_uri;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GDummyFile, g_dummy_file, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+                                               g_dummy_file_file_iface_init))
+
+#define SUB_DELIM_CHARS  "!$&'()*+,;="
+
+static char *       _g_encode_uri       (GDecodedUri *decoded);
+static void         _g_decoded_uri_free (GDecodedUri *decoded);
+static GDecodedUri *_g_decode_uri       (const char  *uri);
+static GDecodedUri *_g_decoded_uri_new  (void);
+
+static char * unescape_string (const gchar *escaped_string,
+                              const gchar *escaped_string_end,
+                              const gchar *illegal_characters);
+
+static void g_string_append_encoded (GString *string, const char *encoded,
+                                    const char *reserved_chars_allowed);
+
+static void
+g_dummy_file_finalize (GObject *object)
+{
+  GDummyFile *dummy;
+
+  dummy = G_DUMMY_FILE (object);
+
+  if (dummy->decoded_uri)
+    _g_decoded_uri_free (dummy->decoded_uri);
+  
+  g_free (dummy->text_uri);
+  
+  if (G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_dummy_file_parent_class)->finalize) (object);
+}
+
+static void
+g_dummy_file_class_init (GDummyFileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_dummy_file_finalize;
+}
+
+static void
+g_dummy_file_init (GDummyFile *dummy)
+{
+}
+
+/**
+ * g_dummy_file_new:
+ * @uri: Universal Resource Identifier for the dummy file object.
+ * 
+ * Returns: a new #GFile. 
+ **/
+GFile *
+g_dummy_file_new (const char *uri)
+{
+  GDummyFile *dummy;
+
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  dummy = g_object_new (G_TYPE_DUMMY_FILE, NULL);
+  dummy->text_uri = g_strdup (uri);
+  dummy->decoded_uri = _g_decode_uri (uri);
+  
+  return G_FILE (dummy);
+}
+
+static gboolean
+g_dummy_file_is_native (GFile *file)
+{
+  return FALSE;
+}
+
+static char *
+g_dummy_file_get_basename (GFile *file)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+  
+  if (dummy->decoded_uri)
+    return g_path_get_basename (dummy->decoded_uri->path);
+  return g_strdup (dummy->text_uri);
+}
+
+static char *
+g_dummy_file_get_path (GFile *file)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+
+  if (dummy->decoded_uri)
+    return g_strdup (dummy->decoded_uri->path);
+  return NULL;
+}
+
+static char *
+g_dummy_file_get_uri (GFile *file)
+{
+  return g_strdup (G_DUMMY_FILE (file)->text_uri);
+}
+
+static char *
+g_dummy_file_get_parse_name (GFile *file)
+{
+  return g_strdup (G_DUMMY_FILE (file)->text_uri);
+}
+
+static GFile *
+g_dummy_file_get_parent (GFile *file)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+  GFile *parent;
+  char *dirname;
+  char *uri;
+  GDecodedUri new_decoded_uri;
+
+  if (dummy->decoded_uri == NULL)
+    return NULL;
+
+  dirname = g_path_get_dirname (dummy->decoded_uri->path);
+  
+  if (strcmp (dirname, ".") == 0)
+    {
+      g_free (dirname);
+      return NULL;
+    }
+  
+  new_decoded_uri = *dummy->decoded_uri;
+  new_decoded_uri.path = dirname;
+  uri = _g_encode_uri (&new_decoded_uri);
+  g_free (dirname);
+  
+  parent = g_dummy_file_new (uri);
+  g_free (uri);
+  
+  return parent;
+}
+
+static GFile *
+g_dummy_file_dup (GFile *file)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+
+  return g_dummy_file_new (dummy->text_uri);
+}
+
+static guint
+g_dummy_file_hash (GFile *file)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+  
+  return g_str_hash (dummy->text_uri);
+}
+
+static gboolean
+g_dummy_file_equal (GFile *file1,
+                   GFile *file2)
+{
+  GDummyFile *dummy1 = G_DUMMY_FILE (file1);
+  GDummyFile *dummy2 = G_DUMMY_FILE (file2);
+
+  return g_str_equal (dummy1->text_uri, dummy2->text_uri);
+}
+
+static int
+safe_strcmp (const char *a, const char *b)
+{
+  if (a == NULL)
+    a = "";
+  if (b == NULL)
+    b = "";
+
+  return strcmp (a, b);
+}
+
+static gboolean
+uri_same_except_path (GDecodedUri *a,
+                     GDecodedUri *b)
+{
+  if (safe_strcmp (a->scheme, b->scheme) != 0)
+    return FALSE;
+  if (safe_strcmp (a->userinfo, b->userinfo) != 0)
+    return FALSE;
+  if (safe_strcmp (a->host, b->host) != 0)
+    return FALSE;
+  if (a->port != b->port)
+    return FALSE;
+
+  return TRUE;
+}
+
+static const char *
+match_prefix (const char *path, const char *prefix)
+{
+  int prefix_len;
+
+  prefix_len = strlen (prefix);
+  if (strncmp (path, prefix, prefix_len) != 0)
+    return NULL;
+  return path + prefix_len;
+}
+
+static gboolean
+g_dummy_file_contains_file (GFile *parent,
+                           GFile *descendant)
+{
+  GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
+  GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
+  const char *remainder;
+
+  if (parent_dummy->decoded_uri != NULL &&
+      descendant_dummy->decoded_uri != NULL)
+    {
+      if (uri_same_except_path (parent_dummy->decoded_uri,
+                               descendant_dummy->decoded_uri)) {
+       remainder = match_prefix (descendant_dummy->decoded_uri->path,
+                                 parent_dummy->decoded_uri->path);
+       if (remainder != NULL && *remainder == '/')
+         {
+           while (*remainder == '/')
+             remainder++;
+           if (*remainder != 0)
+             return TRUE;
+         }
+      }
+    }
+  else
+    {
+      remainder = match_prefix (descendant_dummy->text_uri,
+                               parent_dummy->text_uri);
+      if (remainder != NULL && *remainder == '/')
+         {
+           while (*remainder == '/')
+             remainder++;
+           if (*remainder != 0)
+             return TRUE;
+         }
+    }
+  
+  return FALSE;
+}
+
+static char *
+g_dummy_file_get_relative_path (GFile *parent,
+                               GFile *descendant)
+{
+  GDummyFile *parent_dummy = G_DUMMY_FILE (parent);
+  GDummyFile *descendant_dummy = G_DUMMY_FILE (descendant);
+  const char *remainder;
+
+  if (parent_dummy->decoded_uri != NULL &&
+      descendant_dummy->decoded_uri != NULL)
+    {
+      if (uri_same_except_path (parent_dummy->decoded_uri,
+                               descendant_dummy->decoded_uri)) {
+       remainder = match_prefix (descendant_dummy->decoded_uri->path,
+                                 parent_dummy->decoded_uri->path);
+       if (remainder != NULL && *remainder == '/')
+         {
+           while (*remainder == '/')
+             remainder++;
+           if (*remainder != 0)
+             return g_strdup (remainder);
+         }
+      }
+    }
+  else
+    {
+      remainder = match_prefix (descendant_dummy->text_uri,
+                               parent_dummy->text_uri);
+      if (remainder != NULL && *remainder == '/')
+         {
+           while (*remainder == '/')
+             remainder++;
+           if (*remainder != 0)
+             return unescape_string (remainder, NULL, "/");
+         }
+    }
+  
+  return NULL;
+}
+
+
+static GFile *
+g_dummy_file_resolve_relative_path (GFile *file,
+                                   const char *relative_path)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+  GFile *child;
+  char *uri;
+  GDecodedUri new_decoded_uri;
+  GString *str;
+
+  if (dummy->decoded_uri == NULL)
+    {
+      str = g_string_new (dummy->text_uri);
+      g_string_append (str, "/");
+      g_string_append_encoded (str, relative_path, SUB_DELIM_CHARS ":@/");
+      child = g_dummy_file_new (str->str);
+      g_string_free (str, TRUE);
+    }
+  else
+    {
+      new_decoded_uri = *dummy->decoded_uri;
+      
+      if (g_path_is_absolute (relative_path))
+       new_decoded_uri.path = g_strdup (relative_path);
+      else
+       new_decoded_uri.path = g_build_filename (new_decoded_uri.path, relative_path, NULL);
+      
+      uri = _g_encode_uri (&new_decoded_uri);
+      g_free (new_decoded_uri.path);
+      
+      child = g_dummy_file_new (uri);
+      g_free (uri);
+    }
+
+  return child;
+}
+
+static GFile *
+g_dummy_file_get_child_for_display_name (GFile        *file,
+                                        const char   *display_name,
+                                        GError      **error)
+{
+  return g_file_get_child (file, display_name);
+}
+
+static gboolean
+g_dummy_file_has_uri_scheme (GFile *file,
+                            const char *uri_scheme)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+  
+  if (dummy->decoded_uri)
+    return g_ascii_strcasecmp (uri_scheme, dummy->decoded_uri->scheme) == 0;
+  return FALSE;
+}
+
+static char *
+g_dummy_file_get_uri_scheme (GFile *file)
+{
+  GDummyFile *dummy = G_DUMMY_FILE (file);
+
+  if (dummy->decoded_uri)
+    return g_strdup (dummy->decoded_uri->scheme);
+    
+  return NULL;
+}
+
+
+static void
+g_dummy_file_file_iface_init (GFileIface *iface)
+{
+  iface->dup = g_dummy_file_dup;
+  iface->hash = g_dummy_file_hash;
+  iface->equal = g_dummy_file_equal;
+  iface->is_native = g_dummy_file_is_native;
+  iface->has_uri_scheme = g_dummy_file_has_uri_scheme;
+  iface->get_uri_scheme = g_dummy_file_get_uri_scheme;
+  iface->get_basename = g_dummy_file_get_basename;
+  iface->get_path = g_dummy_file_get_path;
+  iface->get_uri = g_dummy_file_get_uri;
+  iface->get_parse_name = g_dummy_file_get_parse_name;
+  iface->get_parent = g_dummy_file_get_parent;
+  iface->contains_file = g_dummy_file_contains_file;
+  iface->get_relative_path = g_dummy_file_get_relative_path;
+  iface->resolve_relative_path = g_dummy_file_resolve_relative_path;
+  iface->get_child_for_display_name = g_dummy_file_get_child_for_display_name;
+}
+
+/* Uri handling helper functions: */
+
+static int
+unescape_character (const char *scanner)
+{
+  int first_digit;
+  int second_digit;
+  
+  first_digit = g_ascii_xdigit_value (*scanner++);
+  if (first_digit < 0)
+    return -1;
+
+  second_digit = g_ascii_xdigit_value (*scanner++);
+  if (second_digit < 0)
+    return -1;
+
+  return (first_digit << 4) | second_digit;
+}
+
+static char *
+unescape_string (const gchar *escaped_string,
+                const gchar *escaped_string_end,
+                const gchar *illegal_characters)
+{
+  const gchar *in;
+  gchar *out, *result;
+  gint character;
+  
+  if (escaped_string == NULL)
+    return NULL;
+
+  if (escaped_string_end == NULL)
+    escaped_string_end = escaped_string + strlen (escaped_string);
+  
+  result = g_malloc (escaped_string_end - escaped_string + 1);
+       
+  out = result;
+  for (in = escaped_string; in < escaped_string_end; in++) {
+    character = *in;
+    if (*in == '%') {
+      in++;
+      if (escaped_string_end - in < 2)
+       {
+         g_free (result);
+         return NULL;
+       }
+      
+      character = unescape_character (in);
+      
+      /* Check for an illegal character. We consider '\0' illegal here. */
+      if (character <= 0 ||
+         (illegal_characters != NULL &&
+          strchr (illegal_characters, (char)character) != NULL))
+       {
+         g_free (result);
+         return NULL;
+       }
+      in++; /* The other char will be eaten in the loop header */
+    }
+    *out++ = (char)character;
+  }
+  
+  *out = '\0';
+  g_assert (out - result <= strlen (escaped_string));
+  return result;
+}
+
+void
+_g_decoded_uri_free (GDecodedUri *decoded)
+{
+  if (decoded == NULL)
+    return;
+
+  g_free (decoded->scheme);
+  g_free (decoded->query);
+  g_free (decoded->fragment);
+  g_free (decoded->userinfo);
+  g_free (decoded->host);
+  g_free (decoded->path);
+  g_free (decoded);
+}
+
+GDecodedUri *
+_g_decoded_uri_new (void)
+{
+  GDecodedUri *uri;
+
+  uri = g_new0 (GDecodedUri, 1);
+  uri->port = -1;
+
+  return uri;
+}
+
+GDecodedUri *
+_g_decode_uri (const char *uri)
+{
+  GDecodedUri *decoded;
+  const char *p, *in, *hier_part_start, *hier_part_end, *query_start, *fragment_start;
+  char *out;
+  char c;
+
+  /* From RFC 3986 Decodes:
+   * URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+   */ 
+
+  p = uri;
+  
+  /* Decode scheme:
+     scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+  */
+
+  if (!g_ascii_isalpha (*p))
+    return NULL;
+
+  while (1)
+    {
+      c = *p++;
+
+      if (c == ':')
+       break;
+      
+      if (!(g_ascii_isalnum(c) ||
+           c == '+' ||
+           c == '-' ||
+           c == '.'))
+       return NULL;
+    }
+
+  decoded = _g_decoded_uri_new ();
+  
+  decoded->scheme = g_malloc (p - uri);
+  out = decoded->scheme;
+  for (in = uri; in < p - 1; in++)
+    *out++ = g_ascii_tolower (*in);
+  *out = 0;
+
+  hier_part_start = p;
+
+  query_start = strchr (p, '?');
+  if (query_start)
+    {
+      hier_part_end = query_start++;
+      fragment_start = strchr (query_start, '#');
+      if (fragment_start)
+       {
+         decoded->query = g_strndup (query_start, fragment_start - query_start);
+         decoded->fragment = g_strdup (fragment_start+1);
+       }
+      else
+       {
+         decoded->query = g_strdup (query_start);
+         decoded->fragment = NULL;
+       }
+    }
+  else
+    {
+      /* No query */
+      decoded->query = NULL;
+      fragment_start = strchr (p, '#');
+      if (fragment_start)
+       {
+         hier_part_end = fragment_start++;
+         decoded->fragment = g_strdup (fragment_start);
+       }
+      else
+       {
+         hier_part_end = p + strlen (p);
+         decoded->fragment = NULL;
+       }
+    }
+
+  /*  3:
+      hier-part   = "//" authority path-abempty
+                  / path-absolute
+                  / path-rootless
+                  / path-empty
+
+  */
+
+  if (hier_part_start[0] == '/' &&
+      hier_part_start[1] == '/')
+    {
+      const char *authority_start, *authority_end;
+      const char *userinfo_start, *userinfo_end;
+      const char *host_start, *host_end;
+      const char *port_start;
+      
+      authority_start = hier_part_start + 2;
+      /* authority is always followed by / or nothing */
+      authority_end = memchr (authority_start, '/', hier_part_end - authority_start);
+      if (authority_end == NULL)
+       authority_end = hier_part_end;
+
+      /* 3.2:
+             authority   = [ userinfo "@" ] host [ ":" port ]
+      */
+
+      userinfo_end = memchr (authority_start, '@', authority_end - authority_start);
+      if (userinfo_end)
+       {
+         userinfo_start = authority_start;
+         decoded->userinfo = unescape_string (userinfo_start, userinfo_end, NULL);
+         if (decoded->userinfo == NULL)
+           {
+             _g_decoded_uri_free (decoded);
+             return NULL;
+           }
+         host_start = userinfo_end + 1;
+       }
+      else
+       host_start = authority_start;
+
+      port_start = memchr (host_start, ':', authority_end - host_start);
+      if (port_start)
+       {
+         host_end = port_start++;
+
+         decoded->port = atoi(port_start);
+       }
+      else
+       {
+         host_end = authority_end;
+         decoded->port = -1;
+       }
+
+      decoded->host = g_strndup (host_start, host_end - host_start);
+
+      hier_part_start = authority_end;
+    }
+
+  decoded->path = unescape_string (hier_part_start, hier_part_end, "/");
+
+  if (decoded->path == NULL)
+    {
+      _g_decoded_uri_free (decoded);
+      return NULL;
+    }
+  
+  return decoded;
+}
+
+static gboolean
+is_valid (char c, const char *reserved_chars_allowed)
+{
+  if (g_ascii_isalnum (c) ||
+      c == '-' ||
+      c == '.' ||
+      c == '_' ||
+      c == '~')
+    return TRUE;
+
+  if (reserved_chars_allowed &&
+      strchr (reserved_chars_allowed, c) != NULL)
+    return TRUE;
+  
+  return FALSE;
+}
+
+static void
+g_string_append_encoded (GString *string, const char *encoded,
+                        const char *reserved_chars_allowed)
+{
+  unsigned char c;
+  const char *end;
+  static const gchar hex[16] = "0123456789ABCDEF";
+
+  end = encoded + strlen (encoded);
+  
+  while ((c = *encoded) != 0)
+    {
+      if (is_valid (c, reserved_chars_allowed))
+       {
+         g_string_append_c (string, c);
+         encoded++;
+       }
+      else
+       {
+         g_string_append_c (string, '%');
+         g_string_append_c (string, hex[((guchar)c) >> 4]);
+         g_string_append_c (string, hex[((guchar)c) & 0xf]);
+         encoded++;
+       }
+    }
+}
+
+static char *
+_g_encode_uri (GDecodedUri *decoded)
+{
+  GString *uri;
+
+  uri = g_string_new (NULL);
+
+  g_string_append (uri, decoded->scheme);
+  g_string_append (uri, "://");
+
+  if (decoded->host != NULL)
+    {
+      if (decoded->userinfo)
+       {
+         /* userinfo    = *( unreserved / pct-encoded / sub-delims / ":" ) */
+         g_string_append_encoded (uri, decoded->userinfo, SUB_DELIM_CHARS ":");
+         g_string_append_c (uri, '@');
+       }
+      
+      g_string_append (uri, decoded->host);
+      
+      if (decoded->port != -1)
+       {
+         g_string_append_c (uri, ':');
+         g_string_append_printf (uri, "%d", decoded->port);
+       }
+    }
+
+  g_string_append_encoded (uri, decoded->path, SUB_DELIM_CHARS ":@/");
+  
+  if (decoded->query)
+    {
+      g_string_append_c (uri, '?');
+      g_string_append (uri, decoded->query);
+    }
+    
+  if (decoded->fragment)
+    {
+      g_string_append_c (uri, '#');
+      g_string_append (uri, decoded->fragment);
+    }
+
+  return g_string_free (uri, FALSE);
+}
diff --git a/gio/gdummyfile.h b/gio/gdummyfile.h
new file mode 100644 (file)
index 0000000..43e4517
--- /dev/null
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_DUMMY_FILE_H__
+#define __G_DUMMY_FILE_H__
+
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DUMMY_FILE         (g_dummy_file_get_type ())
+#define G_DUMMY_FILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DUMMY_FILE, GDummyFile))
+#define G_DUMMY_FILE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DUMMY_FILE, GDummyFileClass))
+#define G_IS_DUMMY_FILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DUMMY_FILE))
+#define G_IS_DUMMY_FILE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DUMMY_FILE))
+#define G_DUMMY_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DUMMY_FILE, GDummyFileClass))
+
+typedef struct _GDummyFile        GDummyFile;
+typedef struct _GDummyFileClass   GDummyFileClass;
+
+struct _GDummyFileClass
+{
+  GObjectClass parent_class;
+};
+
+GType g_dummy_file_get_type (void) G_GNUC_CONST;
+  
+GFile * g_dummy_file_new (const char *uri);
+
+G_END_DECLS
+
+#endif /* __G_DUMMY_FILE_H__ */
diff --git a/gio/gfile.c b/gio/gfile.c
new file mode 100644 (file)
index 0000000..617422f
--- /dev/null
@@ -0,0 +1,4345 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#include "gfile.h"
+#include "gvfs.h"
+#include "gioscheduler.h"
+#include <glocalfile.h>
+#include "gsimpleasyncresult.h"
+#include "gpollfilemonitor.h"
+#include "glibintl.h"
+
+static void g_file_base_init (gpointer g_class);
+static void g_file_class_init (gpointer g_class,
+                              gpointer class_data);
+
+static void               g_file_real_query_info_async          (GFile                *file,
+                                                                const char           *attributes,
+                                                                GFileQueryInfoFlags   flags,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFileInfo *        g_file_real_query_info_finish         (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static void               g_file_real_enumerate_children_async  (GFile                *file,
+                                                                const char           *attributes,
+                                                                GFileQueryInfoFlags   flags,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFileEnumerator *  g_file_real_enumerate_children_finish (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static void               g_file_real_read_async                (GFile                *file,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFileInputStream * g_file_real_read_finish               (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static void               g_file_real_append_to_async           (GFile                *file,
+                                                                GFileCreateFlags      flags,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFileOutputStream *g_file_real_append_to_finish          (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static void               g_file_real_create_async              (GFile                *file,
+                                                                GFileCreateFlags      flags,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFileOutputStream *g_file_real_create_finish             (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static void               g_file_real_replace_async             (GFile                *file,
+                                                                const char           *etag,
+                                                                gboolean              make_backup,
+                                                                GFileCreateFlags      flags,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFileOutputStream *g_file_real_replace_finish            (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static gboolean           g_file_real_set_attributes_from_info  (GFile                *file,
+                                                                GFileInfo            *info,
+                                                                GFileQueryInfoFlags   flags,
+                                                                GCancellable         *cancellable,
+                                                                GError              **error);
+static void               g_file_real_set_display_name_async    (GFile                *file,
+                                                                const char           *display_name,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static GFile *            g_file_real_set_display_name_finish   (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GError              **error);
+static void               g_file_real_set_attributes_async      (GFile                *file,
+                                                                GFileInfo            *info,
+                                                                GFileQueryInfoFlags   flags,
+                                                                int                   io_priority,
+                                                                GCancellable         *cancellable,
+                                                                GAsyncReadyCallback   callback,
+                                                                gpointer              user_data);
+static gboolean           g_file_real_set_attributes_finish     (GFile                *file,
+                                                                GAsyncResult         *res,
+                                                                GFileInfo           **info,
+                                                                GError              **error);
+
+GType
+g_file_get_type (void)
+{
+  static GType file_type = 0;
+
+  if (! file_type)
+    {
+      static const GTypeInfo file_info =
+      {
+        sizeof (GFileIface), /* class_size */
+       g_file_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       g_file_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      file_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GFile"),
+                               &file_info, 0);
+
+      g_type_interface_add_prerequisite (file_type, G_TYPE_OBJECT);
+    }
+
+  return file_type;
+}
+
+static void
+g_file_class_init (gpointer g_class,
+                  gpointer class_data)
+{
+  GFileIface *iface = g_class;
+
+  iface->enumerate_children_async = g_file_real_enumerate_children_async;
+  iface->enumerate_children_finish = g_file_real_enumerate_children_finish;
+  iface->set_display_name_async = g_file_real_set_display_name_async;
+  iface->set_display_name_finish = g_file_real_set_display_name_finish;
+  iface->query_info_async = g_file_real_query_info_async;
+  iface->query_info_finish = g_file_real_query_info_finish;
+  iface->set_attributes_async = g_file_real_set_attributes_async;
+  iface->set_attributes_finish = g_file_real_set_attributes_finish;
+  iface->read_async = g_file_real_read_async;
+  iface->read_finish = g_file_real_read_finish;
+  iface->append_to_async = g_file_real_append_to_async;
+  iface->append_to_finish = g_file_real_append_to_finish;
+  iface->create_async = g_file_real_create_async;
+  iface->create_finish = g_file_real_create_finish;
+  iface->replace_async = g_file_real_replace_async;
+  iface->replace_finish = g_file_real_replace_finish;
+  iface->set_attributes_from_info = g_file_real_set_attributes_from_info;
+}
+
+static void
+g_file_base_init (gpointer g_class)
+{
+}
+
+
+/**
+ * g_file_is_native:
+ * @file: input #GFile.
+ *
+ * Returns: %TRUE if file is native.
+ * TODO: Explain what "native" means.
+ **/
+gboolean
+g_file_is_native (GFile *file)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->is_native) (file);
+}
+
+
+/**
+ * g_file_has_uri_scheme: 
+ * @file: input #GFile.
+ * @uri_scheme: a string containing a URI scheme.
+ *
+ * Returns: %TRUE if #GFile's backend supports the
+ * given URI scheme, FALSE if URI scheme is %NULL,
+ * not supported, or #GFile is invalid.
+ **/
+gboolean
+g_file_has_uri_scheme (GFile *file,
+                      const char *uri_scheme)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (uri_scheme != NULL, FALSE);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->has_uri_scheme) (file, uri_scheme);
+}
+
+
+/**
+ * g_file_get_uri_scheme:
+ * @file: input #GFile.
+ *
+ * Returns: string to the URI scheme for the given #GFile.  
+ * RFC 3986 decodes the scheme as:
+ * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] 
+ * Popular schemes include "file", "http", "svn", etc.
+ *
+ * The returned string should be freed when no longer needed.
+ **/
+char *
+g_file_get_uri_scheme (GFile *file)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_uri_scheme) (file);
+}
+
+
+/**
+ * g_file_get_basename:
+ * @file: input #GFile.
+ *
+ * Returns: string containing the #GFile's base name, or %NULL if given
+ * #GFile is invalid. 
+ *
+ * The returned string should be freed when no longer needed
+ **/
+char *
+g_file_get_basename (GFile *file)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_basename) (file);
+}
+
+/**
+ * g_file_get_path:
+ * @file: input #GFile.
+ *
+ * Returns: string containing the #GFile's path, or %NULL if given
+ * #GFile is invalid. 
+ * 
+ * The returned string should be freed when no longer needed
+ **/
+char *
+g_file_get_path (GFile *file)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_path) (file);
+}
+
+/**
+ * g_file_get_uri:
+ * @file: input #GFile.
+ *
+ * Returns: string to the #GFile's Universal Resource Identifier (URI), 
+ * or %NULL if given #GFile is invalid. 
+ * 
+ * The returned string should be freed when no longer needed
+ **/
+char *
+g_file_get_uri (GFile *file)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_uri) (file);
+}
+
+/**
+ * g_file_parse_name:
+ * @file: input #GFile.
+ *
+ * Returns: string to the GFile's parsed name, or %NULL if given
+ * GFile is invalid. 
+ * 
+ * The returned string should be freed when no longer needed
+ **/
+char *
+g_file_get_parse_name (GFile *file)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_parse_name) (file);
+}
+
+/**
+ * g_file_dup:
+ * @file: input #GFile.
+ *
+ * Returns: #GFile that is a duplicate of the given #GFile, 
+ * or %NULL if given #GFile is invalid. 
+ **/
+GFile *
+g_file_dup (GFile *file)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->dup) (file);
+}
+
+/**
+ * g_file_hash:
+ * @file: #gconstpointer to a #GFile.
+ *
+ * Returns: 0 if @file is not a #GFile, otherwise a 
+ * guint containing a hash of the #GFile. This function
+ * is intended for easily hashing a #GFile to add to a 
+ * #GHashTable or similar data structure.
+ * 
+ **/
+guint
+g_file_hash (gconstpointer file)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), 0);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->hash) ((GFile *)file);
+}
+
+/**
+ * g_file_equal:
+ * @file1: the first #GFile.
+ * @file2: the second #GFile.
+ *
+ * Returns: %TRUE if @file1 and @file2 are equal.
+ * %FALSE if either is not a #GFile.
+ **/
+gboolean
+g_file_equal (GFile *file1,
+             GFile *file2)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file1), FALSE);
+  g_return_val_if_fail (G_IS_FILE (file2), FALSE);
+  
+  if (G_TYPE_FROM_INSTANCE (file1) != G_TYPE_FROM_INSTANCE (file2))
+    return FALSE;
+
+  iface = G_FILE_GET_IFACE (file1);
+  
+  return (* iface->equal) (file1, file2);
+}
+
+
+/**
+ * g_file_get_parent:
+ * @file: input #GFile.
+ *
+ * Returns: a #GFile structure to the parent of the given
+ * #GFile. 
+ * 
+ * This function should return the parent directory of the given
+ * @file. If the @file represents the root directory of the 
+ * file system, then %NULL will be returned.
+ **/
+GFile *
+g_file_get_parent (GFile *file)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_parent) (file);
+}
+
+/**
+ * g_file_get_child:
+ * @file: input #GFile.
+ * @name: string containing the child's name.
+ *
+ * Returns: a #GFile to a child specified by 
+ * @name or %NULL if @name is %NULL, or the specified
+ * child doesn't exist.
+ **/
+GFile *
+g_file_get_child (GFile *file,
+                 const char *name)
+{
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+
+  return g_file_resolve_relative_path (file, name);
+}
+
+/**
+ * g_file_get_child_for_display_name:
+ * @file: input #GFile.
+ * @display_name: string to a possible child.
+ * @error: #GError.
+ * 
+ * Returns: %NULL if @display_name is %NULL, #GFile to 
+ * the specified child.
+ **/
+GFile *
+g_file_get_child_for_display_name (GFile *file,
+                                  const char *display_name,
+                                  GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (display_name != NULL, NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->get_child_for_display_name) (file, display_name, error);
+}
+
+/**
+ * g_file_contains_file:
+ * @parent: input #GFile.
+ * @descendant: input #GFile.
+ * 
+ * Returns: %FALSE if either the @parent or @descendant 
+ * is invalid. %TRUE if the @descendent's parent is @parent.
+ **/
+gboolean
+g_file_contains_file (GFile *parent,
+                     GFile *descendant)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (parent), FALSE);
+  g_return_val_if_fail (G_IS_FILE (descendant), FALSE);
+
+  if (G_TYPE_FROM_INSTANCE (parent) != G_TYPE_FROM_INSTANCE (descendant))
+    return FALSE;
+  
+  iface = G_FILE_GET_IFACE (parent);
+
+  return (* iface->contains_file) (parent, descendant);
+}
+
+/**
+ * g_file_get_relative_path:
+ * @parent: input #GFile.
+ * @descendant: input #GFile.
+ *
+ * Returns: string with the relative path from 
+ * @descendant to @parent. 
+ *
+ * The returned string should be freed when no longer needed
+ **/
+char *
+g_file_get_relative_path (GFile *parent,
+                         GFile *descendant)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (parent), NULL);
+  g_return_val_if_fail (G_IS_FILE (descendant), NULL);
+
+  if (G_TYPE_FROM_INSTANCE (parent) != G_TYPE_FROM_INSTANCE (descendant))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (parent);
+
+  return (* iface->get_relative_path) (parent, descendant);
+}
+
+/**
+ * g_file_resolve_relative_path:
+ * @file: input #GFile.
+ * @relative_path: a given relative path string.
+ *
+ * Returns: #GFile to the resolved path. %NULL if @relative_path is NULL.
+ * or if @file is invalid.
+ **/
+GFile *
+g_file_resolve_relative_path (GFile *file,
+                             const char *relative_path)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (relative_path != NULL, NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->resolve_relative_path) (file, relative_path);
+}
+
+/**
+ * g_file_enumerate_children:
+ * @file: input #GFile.
+ * @attributes: a string.
+ * @flags: #GFileQueryInfoFlags field.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: #GError for error reporting.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: %NULL if cancelled or if #GFile's backend doesn't
+ * support #GFileEnumerator. Returns a #GFileEnumerator if successful.
+ **/
+GFileEnumerator *
+g_file_enumerate_children (GFile *file,
+                          const char *attributes,
+                          GFileQueryInfoFlags flags,
+                          GCancellable *cancellable,
+                          GError **error)
+                          
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->enumerate_children == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+
+  return (* iface->enumerate_children) (file, attributes, flags,
+                                       cancellable, error);
+}
+
+/**
+ * g_file_enumerate_children_async:
+ * @file: input #GFile.
+ * @attributes: a string.
+ * @flags: a set of #GFileQueryInfoFlags.
+ * @io_priority: the io priority of the request. the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: a #gpointer.
+ *
+ * Asynchronously enumerates the children of the given @file. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ **/
+void
+g_file_enumerate_children_async (GFile                      *file,
+                                const char                 *attributes,
+                                GFileQueryInfoFlags         flags,
+                                int                         io_priority,
+                                GCancellable               *cancellable,
+                                GAsyncReadyCallback         callback,
+                                gpointer                    user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->enumerate_children_async) (file,
+                                      attributes,
+                                      flags,
+                                      io_priority,
+                                      cancellable,
+                                      callback,
+                                      user_data);
+}
+
+/**
+ * g_file_enumerate_children_finish:
+ * @file: input #GFile.
+ * @res: a #GAsyncResult.
+ * @error: a #GError.
+ * 
+ * Returns: a #GFileEnumerator or %NULL if an error occurred.
+ **/
+GFileEnumerator *
+g_file_enumerate_children_finish  (GFile                      *file,
+                                  GAsyncResult               *res,
+                                  GError                    **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->enumerate_children_finish) (file, res, error);
+}
+
+
+/**
+ * g_file_query_info:
+ * @file: input #GFile.
+ * @attributes: a string.
+ * @flags: a set of #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: a #GFileInfo for the given @file, or %NULL on error.
+ **/
+GFileInfo *
+g_file_query_info (GFile *file,
+                  const char *attributes,
+                  GFileQueryInfoFlags flags,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->query_info == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+  
+  return (* iface->query_info) (file, attributes, flags, cancellable, error);
+}
+
+/**
+ * g_file_query_info_async:
+ * @file: input #GFile.
+ * @attributes: a string.
+ * @flags: a set of #GFileQueryInfoFlags.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_query_info_async (GFile                      *file,
+                        const char                 *attributes,
+                        GFileQueryInfoFlags         flags,
+                        int                         io_priority,
+                        GCancellable               *cancellable,
+                        GAsyncReadyCallback         callback,
+                        gpointer                    user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->query_info_async) (file,
+                              attributes,
+                              flags,
+                              io_priority,
+                              cancellable,
+                              callback,
+                              user_data);
+}
+
+/**
+ * g_file_query_info_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @error: a #GError. 
+ * 
+ * Finishes an asynchronous file info query. If the operation failed, 
+ * returns %NULL and set @error appropriately if present.
+ * 
+ * Returns: #GFileInfo for given @file or %NULL.
+ **/
+GFileInfo *
+g_file_query_info_finish (GFile                      *file,
+                         GAsyncResult               *res,
+                         GError                    **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->query_info_finish) (file, res, error);
+}
+
+/**
+ * g_file_query_filesystem_info:
+ * @file: input #GFile.
+ * @attributes: a string. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: a #GFileInfo or %NULl if there was an error.
+ **/
+GFileInfo *
+g_file_query_filesystem_info (GFile *file,
+                             const char *attributes,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->query_filesystem_info == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+  
+  return (* iface->query_filesystem_info) (file, attributes, cancellable, error);
+}
+
+/**
+ * g_file_find_enclosing_volume:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: a #GVolume where the @file is located.
+ **/
+GVolume *
+g_file_find_enclosing_volume (GFile *file,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  iface = G_FILE_GET_IFACE (file);
+  if (iface->find_enclosing_volume == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_FOUND,
+                  _("Containing volume does not exist"));
+      return NULL;
+    }
+  
+  return (* iface->find_enclosing_volume) (file, cancellable, error);
+}
+
+/**
+ * g_file_read:
+ * @file: #GFile to read.
+ * @cancellable: a #GCancellable
+ * @error: a #GError.
+ *
+ * Reads a whole file into a #GFileInputStream. Fails returning %NULL if 
+ * given #GFile points to a directory.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: #GFileInputStream or %NULL.
+ **/
+GFileInputStream *
+g_file_read (GFile *file,
+            GCancellable *cancellable,
+            GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->read == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+  
+  return (* iface->read) (file, cancellable, error);
+}
+
+/**
+ * g_file_append_to:
+ * @file: input #GFile.
+ * @flags: a set of #GFileCreateFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: a #GFileOutputStream.
+ **/
+GFileOutputStream *
+g_file_append_to (GFile *file,
+                 GFileCreateFlags flags,
+                 GCancellable *cancellable,
+                 GError **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->append_to == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+  
+  return (* iface->append_to) (file, flags, cancellable, error);
+}
+
+/**
+ * g_file_create:
+ * @file: input #GFile.
+ * @flags: a set of #GFileCreateFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: a #GFileOutputStream for the newly created file, or 
+ * %NULL on error.
+ **/
+GFileOutputStream *
+g_file_create (GFile *file,
+              GFileCreateFlags flags,
+              GCancellable *cancellable,
+              GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->create == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+  
+  return (* iface->create) (file, flags, cancellable, error);
+}
+
+/**
+ * g_file_replace:
+ * @file: input #GFile.
+ * @etag: an Entity Tag for the current #GFile.
+ * @make_backup: a #gboolean.
+ * @flags: a set of #GFileCreateFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: a #GFileOutputStream or %NULL on error. If @make_backup is %TRUE, 
+ * this function will attempt to make a backup of the current file.
+ **/
+GFileOutputStream *
+g_file_replace (GFile *file,
+               const char *etag,
+               gboolean  make_backup,
+               GFileCreateFlags flags,
+               GCancellable *cancellable,
+               GError **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->replace == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return NULL;
+    }
+  
+  
+  /* Handle empty tag string as NULL in consistent way. */
+  if (etag && *etag == 0)
+    etag = NULL;
+  
+  return (* iface->replace) (file, etag, make_backup, flags, cancellable, error);
+}
+
+/**
+ * g_file_read_async:
+ * @file: input #GFile.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: a #gpointer. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ * Asynchronously reads @file. For the synchronous version of this function, 
+ * see g_file_read().
+ **/
+void
+g_file_read_async (GFile                  *file,
+                  int                     io_priority,
+                  GCancellable           *cancellable,
+                  GAsyncReadyCallback     callback,
+                  gpointer                user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->read_async) (file,
+                        io_priority,
+                        cancellable,
+                        callback,
+                        user_data);
+}
+
+/**
+ * g_file_read_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @error: a #GError. 
+ * 
+ * Returns: a #GFileInputStream or %NULL if there was an error.
+ **/
+GFileInputStream *
+g_file_read_finish (GFile                  *file,
+                   GAsyncResult           *res,
+                   GError                **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->read_finish) (file, res, error);
+}
+
+/**
+ * g_file_append_to_async:
+ * @file: input #GFile.
+ * @flags: a set of #GFileCreateFlags.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Readies a file for appending data asynchronously. 
+ * For the synchronous version of this function, see  
+ * g_file_append_to().
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_append_to_async (GFile                      *file,
+                       GFileCreateFlags            flags,
+                       int                         io_priority,
+                       GCancellable               *cancellable,
+                       GAsyncReadyCallback         callback,
+                       gpointer                    user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->append_to_async) (file,
+                             flags,
+                             io_priority,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+/**
+ * g_file_append_to_finish:
+ * @file: input #GFile.
+ * @res: #GAsyncResult
+ * @error: a #GError. 
+ * 
+ * Returns: a valid #GFileOutputStream or %NULL upon error.
+ **/
+GFileOutputStream *
+g_file_append_to_finish (GFile                      *file,
+                        GAsyncResult               *res,
+                        GError                    **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->append_to_finish) (file, res, error);
+}
+
+/**
+ * g_file_create_async:
+ * @file: input #GFile.
+ * @flags: a set of #GFileCreateFlags.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Creates a new file asynchronously. For the synchronous version of 
+ * this function, see g_file_create(). 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ **/
+void
+g_file_create_async (GFile                      *file,
+                    GFileCreateFlags            flags,
+                    int                         io_priority,
+                    GCancellable               *cancellable,
+                    GAsyncReadyCallback         callback,
+                    gpointer                    user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->create_async) (file,
+                          flags,
+                          io_priority,
+                          cancellable,
+                          callback,
+                          user_data);
+}
+
+/**
+ * g_file_create_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @error: a #GError. 
+ * 
+ * Returns: a #GFileOutputStream.
+ **/
+GFileOutputStream *
+g_file_create_finish (GFile                      *file,
+                     GAsyncResult               *res,
+                     GError                    **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->create_finish) (file, res, error);
+}
+
+/**
+ * g_file_replace_async:
+ * @file: input #GFile.
+ * @etag: an Entity Tag for the current #GFile.
+ * @make_backup: a #gboolean.
+ * @flags: a set of #GFileCreateFlags.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Replaces a file's contents. For the synchronous version of this 
+ * function, see g_file_replace(). If @make_backup is %TRUE, this function
+ * will attempt to make a backup of the current file.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_replace_async (GFile                      *file,
+                     const char                 *etag,
+                     gboolean                    make_backup,
+                     GFileCreateFlags            flags,
+                     int                         io_priority,
+                     GCancellable               *cancellable,
+                     GAsyncReadyCallback         callback,
+                     gpointer                    user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->replace_async) (file,
+                           etag,
+                           make_backup,
+                           flags,
+                           io_priority,
+                           cancellable,
+                           callback,
+                           user_data);
+}
+
+/**
+ * g_file_replace_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @error: a #GError. 
+ * 
+ * Finishes replacing the contents of the file started by 
+ * g_file_replace_async().
+ * 
+ * Returns: a #GFileOutputStream, or %NULL if an error has occured.
+ **/
+GFileOutputStream *
+g_file_replace_finish (GFile                      *file,
+                      GAsyncResult               *res,
+                      GError                    **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->replace_finish) (file, res, error);
+}
+
+static gboolean
+copy_symlink (GFile                  *destination,
+             GFileCopyFlags          flags,
+             GCancellable           *cancellable,
+             const char             *target,
+             GError                **error)
+{
+  GError *my_error;
+  gboolean tried_delete;
+  GFileInfo *info;
+  GFileType file_type;
+
+  tried_delete = FALSE;
+
+ retry:
+  my_error = NULL;
+  if (!g_file_make_symbolic_link (destination, target, cancellable, &my_error))
+    {
+      /* Maybe it already existed, and we want to overwrite? */
+      if (!tried_delete && (flags & G_FILE_COPY_OVERWRITE) && 
+         my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_EXISTS)
+       {
+         g_error_free (my_error);
+
+
+         /* Don't overwrite if the destination is a directory */
+         info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STD_TYPE,
+                                   G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                   cancellable, &my_error);
+         if (info != NULL)
+           {
+             file_type = g_file_info_get_file_type (info);
+             g_object_unref (info);
+             
+             if (file_type == G_FILE_TYPE_DIRECTORY)
+               {
+                 g_set_error (error, G_IO_ERROR, G_IO_ERROR_IS_DIRECTORY,
+                              _("Can't copy over directory"));
+                 return FALSE;
+               }
+           }
+         
+         if (!g_file_delete (destination, cancellable, error))
+           return FALSE;
+         
+         tried_delete = TRUE;
+         goto retry;
+       }
+            /* Nah, fail */
+      g_propagate_error (error, my_error);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static GInputStream *
+open_source_for_copy (GFile *source,
+                     GFile *destination,
+                     GFileCopyFlags flags,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+  GError *my_error;
+  GInputStream *in;
+  GFileInfo *info;
+  GFileType file_type;
+  
+  my_error = NULL;
+  in = (GInputStream *)g_file_read (source, cancellable, &my_error);
+  if (in != NULL)
+    return in;
+
+  /* There was an error opening the source, try to set a good error for it: */
+
+  if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_IS_DIRECTORY)
+    {
+      /* The source is a directory, don't fail with WOULD_RECURSE immediately, as
+        that is less useful to the app. Better check for errors on the target instead. */
+      
+      g_error_free (my_error);
+      my_error = NULL;
+      
+      info = g_file_query_info (destination, G_FILE_ATTRIBUTE_STD_TYPE,
+                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                               cancellable, &my_error);
+      if (info != NULL)
+       {
+         file_type = g_file_info_get_file_type (info);
+         g_object_unref (info);
+         
+         if (flags & G_FILE_COPY_OVERWRITE)
+           {
+             if (file_type == G_FILE_TYPE_DIRECTORY)
+               {
+                 g_set_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_MERGE,
+                              _("Can't copy directory over directory"));
+                 return NULL;
+               }
+             /* continue to would_recurse error */
+           }
+         else
+           {
+             g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS,
+                          _("Target file exists"));
+             return NULL;
+           }
+       }
+      else
+       {
+         /* Error getting info from target, return that error (except for NOT_FOUND, which is no error here) */
+         if (my_error->domain != G_IO_ERROR && my_error->code != G_IO_ERROR_NOT_FOUND)
+           {
+             g_propagate_error (error, my_error);
+             return NULL;
+           }
+         g_error_free (my_error);
+       }
+      
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_WOULD_RECURSE,
+                  _("Can't recursively copy directory"));
+      return NULL;
+    }
+
+  g_propagate_error (error, my_error);
+  return NULL;
+}
+
+static gboolean
+should_copy (GFileAttributeInfo *info, gboolean as_move)
+{
+  if (as_move)
+    return info->flags & G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED;
+  return info->flags & G_FILE_ATTRIBUTE_FLAGS_COPY_WITH_FILE;
+}
+
+static char *
+build_attribute_list_for_copy (GFileAttributeInfoList *attributes,
+                              GFileAttributeInfoList *namespaces,
+                              gboolean as_move)
+{
+  GString *s;
+  gboolean first;
+  int i;
+  
+  first = TRUE;
+  s = g_string_new ("");
+
+  if (attributes)
+    {
+      for (i = 0; i < attributes->n_infos; i++)
+       {
+         if (should_copy (&attributes->infos[i], as_move))
+           {
+             if (first)
+               first = FALSE;
+             else
+               g_string_append_c (s, ',');
+               
+             g_string_append (s, attributes->infos[i].name);
+           }
+       }
+    }
+
+  if (namespaces)
+    {
+      for (i = 0; i < namespaces->n_infos; i++)
+       {
+         if (should_copy (&namespaces->infos[i], as_move))
+           {
+             if (first)
+               first = FALSE;
+             else
+               g_string_append_c (s, ',');
+               
+             g_string_append (s, namespaces->infos[i].name);
+             g_string_append (s, ":*");
+           }
+       }
+    }
+
+  return g_string_free (s, FALSE);
+
+}
+
+gboolean
+g_file_copy_attributes (GFile *source,
+                       GFile *destination,
+                       GFileCopyFlags flags,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+  GFileAttributeInfoList *attributes, *namespaces;
+  char *attrs_to_read;
+  gboolean res;
+  GFileInfo *info;
+  gboolean as_move;
+  gboolean source_nofollow_symlinks;
+
+  as_move = flags & G_FILE_COPY_ALL_METADATA;
+  source_nofollow_symlinks = flags & G_FILE_COPY_NOFOLLOW_SYMLINKS;
+
+  /* Ignore errors here, if the target supports no attributes there is nothing to copy */
+  attributes = g_file_query_settable_attributes (destination, cancellable, NULL);
+  namespaces = g_file_query_writable_namespaces (destination, cancellable, NULL);
+
+  if (attributes == NULL && namespaces == NULL)
+    return TRUE;
+
+  attrs_to_read = build_attribute_list_for_copy (attributes, namespaces, as_move);
+
+  /* Ignore errors here, if we can't read some info (e.g. if it doesn't exist)
+     we just don't copy it. */
+  info = g_file_query_info (source, attrs_to_read,
+                           source_nofollow_symlinks ? G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS:0,
+                           cancellable,
+                           NULL);
+
+  g_free (attrs_to_read);
+  
+  res = TRUE;
+  if  (info)
+    {
+      res = g_file_set_attributes_from_info (destination,
+                                            info, 0,
+                                            cancellable,
+                                            error);
+      g_object_unref (info);
+    }
+  
+  g_file_attribute_info_list_unref (attributes);
+  g_file_attribute_info_list_unref (namespaces);
+  
+  return res;
+}
+
+/* Closes the streams */
+static gboolean
+copy_stream_with_progress (GInputStream *in,
+                          GOutputStream *out,
+                          GCancellable *cancellable,
+                          GFileProgressCallback progress_callback,
+                          gpointer progress_callback_data,
+                          GError **error)
+{
+  gssize n_read, n_written;
+  goffset current_size;
+  char buffer[8192], *p;
+  gboolean res;
+  goffset total_size;
+  GFileInfo *info;
+
+  total_size = 0;
+  info = g_file_input_stream_query_info (G_FILE_INPUT_STREAM (in),
+                                        G_FILE_ATTRIBUTE_STD_SIZE,
+                                        cancellable, NULL);
+  if (info)
+    {
+      total_size = g_file_info_get_size (info);
+      g_object_unref (info);
+    }
+  
+  current_size = 0;
+  res = TRUE;
+  while (TRUE)
+    {
+      n_read = g_input_stream_read (in, buffer, sizeof (buffer), cancellable, error);
+      if (n_read == -1)
+       {
+         res = FALSE;
+         break;
+       }
+       
+      if (n_read == 0)
+       break;
+
+      current_size += n_read;
+
+      p = buffer;
+      while (n_read > 0)
+       {
+         n_written = g_output_stream_write (out, p, n_read, cancellable, error);
+         if (n_written == -1)
+           {
+             res = FALSE;
+             break;
+           }
+
+         p += n_written;
+         n_read -= n_written;
+       }
+
+      if (!res)
+        break;
+      
+      if (progress_callback)
+       progress_callback (current_size, total_size, progress_callback_data);
+    }
+
+  if (!res)
+    error = NULL; /* Ignore further errors */
+
+  /* Make sure we send full copied size */
+  if (progress_callback)
+    progress_callback (current_size, total_size, progress_callback_data);
+
+  
+  /* Don't care about errors in source here */
+  g_input_stream_close (in, cancellable, NULL);
+
+  /* But write errors on close are bad! */
+  if (!g_output_stream_close (out, cancellable, error))
+    res = FALSE;
+
+  g_object_unref (in);
+  g_object_unref (out);
+      
+  return res;
+}
+
+static gboolean
+file_copy_fallback (GFile                  *source,
+                   GFile                  *destination,
+                   GFileCopyFlags          flags,
+                   GCancellable           *cancellable,
+                   GFileProgressCallback   progress_callback,
+                   gpointer                progress_callback_data,
+                   GError                **error)
+{
+  GInputStream *in;
+  GOutputStream *out;
+  GFileInfo *info;
+  const char *target;
+
+  /* Maybe copy the symlink? */
+  if (flags & G_FILE_COPY_NOFOLLOW_SYMLINKS)
+    {
+      info = g_file_query_info (source,
+                               G_FILE_ATTRIBUTE_STD_TYPE "," G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET,
+                               G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                               cancellable,
+                               error);
+      if (info == NULL)
+       return FALSE;
+
+      if (g_file_info_get_file_type (info) == G_FILE_TYPE_SYMBOLIC_LINK &&
+         (target = g_file_info_get_symlink_target (info)) != NULL)
+       {
+         if (!copy_symlink (destination, flags, cancellable, target, error))
+           {
+             g_object_unref (info);
+             return FALSE;
+           }
+         
+         g_object_unref (info);
+         goto copied_file;
+       }
+      
+      g_object_unref (info);
+    }
+  
+  in = open_source_for_copy (source, destination, flags, cancellable, error);
+  if (in == NULL)
+    return FALSE;
+  
+  if (flags & G_FILE_COPY_OVERWRITE)
+    {
+      out = (GOutputStream *)g_file_replace (destination,
+                                            NULL, 0,
+                                            flags & G_FILE_COPY_BACKUP,
+                                            cancellable, error);
+    }
+  else
+    {
+      out = (GOutputStream *)g_file_create (destination, 0, cancellable, error);
+    }
+
+  if (out == NULL)
+    {
+      g_object_unref (in);
+      return FALSE;
+    }
+
+  if (!copy_stream_with_progress (in, out, cancellable,
+                                 progress_callback, progress_callback_data,
+                                 error))
+    return FALSE;
+
+ copied_file:
+
+  /* Ignore errors here. Failure to copy metadata is not a hard error */
+  g_file_copy_attributes (source, destination,
+                         flags, cancellable, NULL);
+  
+  return TRUE;
+}
+
+/**
+ * g_file_copy:
+ * @source: input #GFile.
+ * @destination: destination #GFile
+ * @flags: set of #GFileCopyFlags
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @progress_callback: function to callback with progress information
+ * @progress_callback_data: userdata to pass to @progress_callback
+ * @error: #GError to set on error
+ * 
+ * List of possible errors resulting from g_file_copy():
+ * source    dest    flags   res
+ *  -        *       *       G_IO_ERROR_NOT_FOUND
+ *  file     -       *       ok
+ *  file     *       0       G_IO_ERROR_EXISTS
+ *  file     file    overwr  ok
+ *  file     dir     overwr  G_IO_ERROR_IS_DIRECTORY
+ *  
+ *  dir      -       *       G_IO_ERROR_WOULD_RECURSE
+ *  dir      *       0       G_IO_ERROR_EXISTS
+ *  dir      dir     overwr  G_IO_ERROR_WOULD_MERGE
+ *  dir      file    overwr  G_IO_ERROR_WOULD_RECURSE
+ * 
+ * Returns: %TRUE on success, %FALSE otherwise.
+ **/
+gboolean
+g_file_copy (GFile                  *source,
+            GFile                  *destination,
+            GFileCopyFlags          flags,
+            GCancellable           *cancellable,
+            GFileProgressCallback   progress_callback,
+            gpointer                progress_callback_data,
+            GError                **error)
+{
+  GFileIface *iface;
+  GError *my_error;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_FILE (source), FALSE);
+  g_return_val_if_fail (G_IS_FILE (destination), FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  if (G_OBJECT_TYPE (source) == G_OBJECT_TYPE (destination))
+    {
+      iface = G_FILE_GET_IFACE (source);
+
+      if (iface->copy)
+       {
+         my_error = NULL;
+         res = (* iface->copy) (source, destination, flags, cancellable, progress_callback, progress_callback_data, &my_error);
+         
+         if (res)
+           return TRUE;
+         
+         if (my_error->domain != G_IO_ERROR || my_error->code != G_IO_ERROR_NOT_SUPPORTED)
+           {
+             g_propagate_error (error, my_error);
+             return FALSE;
+           }
+       }
+    }
+
+  return file_copy_fallback (source, destination, flags, cancellable,
+                            progress_callback, progress_callback_data,
+                            error);
+}
+
+
+/**
+ * g_file_move:
+ * @source: GFile* pointing to the source location.
+ * @destination: GFile* pointing to the destination location.
+ * @flags: GFileCopyFlags enum.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @progress_callback: GFileProgressCallback function for updates.
+ * @progress_callback_data: gpointer to user data for the callback function.
+ * @error: GError for returning error conditions.
+ *
+ * List of possible returns from g_file_move() with given source,
+ * destination, and flags:
+ *
+ * source   dest    flags   results in
+ * -        *       *       G_IO_ERROR_NOT_FOUND
+ * file     -       *       ok
+ * file     *       0       G_IO_ERROR_EXISTS
+ * file     file    overwr  ok
+ * file     dir     overwr  G_IO_ERROR_IS_DIRECTORY
+ * 
+ * dir      -       *       ok || G_IO_ERROR_WOULD_RECURSE
+ * dir      *       0       G_IO_ERROR_EXISTS
+ * dir      dir     overwr  G_IO_ERROR_WOULD_MERGE
+ * dir      file    overwr  ok || G_IO_ERROR_WOULD_RECURSE
+ *
+ * Returns: %TRUE on successful move, %FALSE otherwise.
+ **/
+gboolean
+g_file_move (GFile                  *source,
+            GFile                  *destination,
+            GFileCopyFlags          flags,
+            GCancellable           *cancellable,
+            GFileProgressCallback   progress_callback,
+            gpointer                progress_callback_data,
+            GError                **error)
+{
+  GFileIface *iface;
+  GError *my_error;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_FILE (source), FALSE);
+  g_return_val_if_fail (G_IS_FILE (destination), FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  if (G_OBJECT_TYPE (source) == G_OBJECT_TYPE (destination))
+    {
+      iface = G_FILE_GET_IFACE (source);
+
+      if (iface->move)
+       {
+         my_error = NULL;
+         res = (* iface->move) (source, destination, flags, cancellable, progress_callback, progress_callback_data, &my_error);
+         
+         if (res)
+           return TRUE;
+         
+         if (my_error->domain != G_IO_ERROR || my_error->code != G_IO_ERROR_NOT_SUPPORTED)
+           {
+             g_propagate_error (error, my_error);
+             return FALSE;
+           }
+       }
+    }
+
+  flags |= G_FILE_COPY_ALL_METADATA;
+  if (!g_file_copy (source, destination, flags, cancellable,
+                   progress_callback, progress_callback_data,
+                   error))
+    return FALSE;
+
+  return g_file_delete (source, cancellable, error);
+}
+
+/**
+ * g_file_make_directory
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE on successful creation, %FALSE otherwise.
+ **/
+gboolean
+g_file_make_directory (GFile *file,
+                      GCancellable *cancellable,
+                      GError **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->make_directory == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return FALSE;
+    }
+  
+  return (* iface->make_directory) (file, cancellable, error);
+}
+
+/**
+ * g_file_make_symbolic_link:
+ * @file: input #GFile.
+ * @symlink_value: a string with the name of the new symlink.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE on the creation of a new symlink, %FALSE otherwise.
+ **/
+gboolean
+g_file_make_symbolic_link (GFile *file,
+                          const char *symlink_value,
+                          GCancellable *cancellable,
+                          GError **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (symlink_value != NULL, FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+
+  if (*symlink_value == '\0')
+    {
+      g_set_error (error, G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Invalid symlink value given"));
+      return FALSE;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->make_symbolic_link == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return FALSE;
+    }
+  
+  return (* iface->make_symbolic_link) (file, symlink_value, cancellable, error);
+}
+
+/**
+ * g_file_delete:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the file was deleted. %FALSE otherwise.
+ **/
+gboolean
+g_file_delete (GFile *file,
+              GCancellable *cancellable,
+              GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->delete_file == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return FALSE;
+    }
+  
+  return (* iface->delete_file) (file, cancellable, error);
+}
+
+/**
+ * g_file_trash:
+ * @file: GFile to location to send to trash.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: GError for possible failures.
+ *
+ * This function sends the object to the virtual "Trash" location. If the
+ * GFile interface does not have a corresponding "Trash" location, this function 
+ * returns %FALSE, and will set @error appropriately. 
+ *
+ * Returns: %TRUE on successful trash, %FALSE otherwise.
+ **/
+gboolean
+g_file_trash (GFile *file,
+             GCancellable *cancellable,
+             GError **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->trash == NULL)
+    {
+      g_set_error (error,
+                  G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Trash not supported"));
+      return FALSE;
+    }
+  
+  return (* iface->trash) (file, cancellable, error);
+}
+
+/**
+ * g_file_set_display_name:
+ * @file: input #GFile.
+ * @display_name: a string.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: a #GFile, or %NULL if there was an error.
+ * For the asynchronous version of this function, see 
+ * g_file_set_display_name_async().
+ **/
+GFile *
+g_file_set_display_name (GFile                  *file,
+                        const char             *display_name,
+                        GCancellable           *cancellable,
+                        GError                **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (display_name != NULL, NULL);
+
+  if (strchr (display_name, '/') != NULL)
+    {
+      g_set_error (error,
+                  G_IO_ERROR,
+                  G_IO_ERROR_INVALID_ARGUMENT,
+                  _("File names cannot contain '/'"));
+      return NULL;
+    }
+  
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->set_display_name) (file, display_name, cancellable, error);
+}
+
+/**
+ * g_file_set_display_name_async:
+ * @file: input #GFile.
+ * @display_name: a string.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Asynchronously sets the display name for a given #GFile.
+ * For the synchronous version of this function, see 
+ * g_file_set_display_name().
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_set_display_name_async (GFile                      *file,
+                              const char                 *display_name,
+                              int                         io_priority,
+                              GCancellable               *cancellable,
+                              GAsyncReadyCallback         callback,
+                              gpointer                    user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (display_name != NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->set_display_name_async) (file,
+                                    display_name,
+                                    io_priority,
+                                    cancellable,
+                                    callback,
+                                    user_data);
+}
+
+/**
+ * g_file_set_display_name_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @error: a #GError. 
+ * 
+ * Returns: a #GFile or %NULL on error.
+ **/
+GFile *
+g_file_set_display_name_finish (GFile                      *file,
+                               GAsyncResult               *res,
+                               GError                    **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->set_display_name_finish) (file, res, error);
+}
+
+/**
+ * g_file_query_settable_attributes:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: the type and full attribute name of all the attributes that
+ * the file can set. This doesn't mean setting it will always succeed though,
+ * you might get an access failure, or some specific file may not support a
+ * specific attribute.
+ **/
+GFileAttributeInfoList *
+g_file_query_settable_attributes (GFile                      *file,
+                                 GCancellable               *cancellable,
+                                 GError                    **error)
+{
+  GFileIface *iface;
+  GError *my_error;
+  GFileAttributeInfoList *list;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->query_settable_attributes == NULL)
+    return g_file_attribute_info_list_new ();
+
+  my_error = NULL;
+  list = (* iface->query_settable_attributes) (file, cancellable, &my_error);
+  
+  if (list == NULL)
+    {
+      if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_NOT_SUPPORTED)
+       {
+         list = g_file_attribute_info_list_new ();
+         g_error_free (my_error);
+       }
+      else
+       g_propagate_error (error, my_error);
+    }
+  
+  return list;
+}
+
+/**
+ * g_file_query_writable_namespaces:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: a #GFileAttributeInfoList of attribute namespaces 
+ * where the user can create their own attribute names, such 
+ * as extended attributes.
+ **/
+GFileAttributeInfoList *
+g_file_query_writable_namespaces (GFile                      *file,
+                                 GCancellable               *cancellable,
+                                 GError                    **error)
+{
+  GFileIface *iface;
+  GError *my_error;
+  GFileAttributeInfoList *list;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->query_writable_namespaces == NULL)
+    return g_file_attribute_info_list_new ();
+
+  my_error = NULL;
+  list = (* iface->query_writable_namespaces) (file, cancellable, &my_error);
+  
+  if (list == NULL)
+    {
+      if (my_error->domain == G_IO_ERROR && my_error->code == G_IO_ERROR_NOT_SUPPORTED)
+       {
+         list = g_file_attribute_info_list_new ();
+         g_error_free (my_error);
+       }
+      else
+       g_propagate_error (error, my_error);
+    }
+  
+  return list;
+}
+
+/**
+ * g_file_set_attribute:
+ * @file: input #GFile.
+ * @attribute: a string containing the attribute's name.
+ * @value: #GFileAttributeValue
+ * @flags: #GFileQueryInfoFlags
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the attribute was correctly set, %FALSE
+ * otherwise.
+ **/
+gboolean
+g_file_set_attribute (GFile                  *file,
+                     const char             *attribute,
+                     const GFileAttributeValue    *value,
+                     GFileQueryInfoFlags       flags,
+                     GCancellable           *cancellable,
+                     GError                **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->set_attribute == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Operation not supported"));
+      return FALSE;
+    }
+
+  return (* iface->set_attribute) (file, attribute, value, flags, cancellable, error);
+}
+
+/**
+ * g_file_set_attributes_from_info:
+ * @file: input #GFile.
+ * @info: a #GFileInfo.
+ * @flags: #GFileQueryInfoFlags
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Tries to set all attributes in the GFileInfo on the target values, not stopping
+ * on the first error.
+ * 
+ * Returns: %TRUE if there was any error, and @error will be set to
+ * the first error. Error on particular fields are flagged by setting the
+ * "status" field in the attribute value to %G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING.
+ **/
+gboolean
+g_file_set_attributes_from_info (GFile                      *file,
+                                GFileInfo                  *info,
+                                GFileQueryInfoFlags           flags,
+                                GCancellable               *cancellable,
+                                GError                    **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return FALSE;
+  
+  g_file_info_clear_status (info);
+  
+  iface = G_FILE_GET_IFACE (file);
+
+  return (* iface->set_attributes_from_info) (file, info, flags, cancellable, error);
+}
+
+
+static gboolean
+g_file_real_set_attributes_from_info (GFile                      *file,
+                                     GFileInfo                  *info,
+                                     GFileQueryInfoFlags           flags,
+                                     GCancellable               *cancellable,
+                                     GError                    **error)
+{
+  char **attributes;
+  int i;
+  gboolean res;
+  GFileAttributeValue *value;
+  
+  res = TRUE;
+  
+  attributes = g_file_info_list_attributes (info, NULL);
+
+  for (i = 0; attributes[i] != NULL; i++)
+    {
+      value = (GFileAttributeValue *)g_file_info_get_attribute (info, attributes[i]);
+
+      if (value->status != G_FILE_ATTRIBUTE_STATUS_UNSET)
+       continue;
+
+      if (!g_file_set_attribute (file, attributes[i], value, flags, cancellable, error))
+       {
+         value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
+         res = FALSE;
+         /* Don't set error multiple times */
+         error = NULL;
+       }
+      else
+       value->status = G_FILE_ATTRIBUTE_STATUS_SET;
+    }
+  
+  g_strfreev (attributes);
+  
+  return res;
+}
+
+/**
+ * g_file_set_attributes_async:
+ * @file: input #GFile.
+ * @info: a #GFileInfo.
+ * @flags: a #GFileQueryInfoFlags.
+ * @io_priority: the io priority of the request. the io priority of the request. 
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer.
+ *
+ * Asynchronously sets the attributes of @file with @info and @flags.
+ * For the synchronous version of this function, see g_file_set_attributes(). 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_set_attributes_async (GFile                      *file,
+                            GFileInfo                  *info,
+                            GFileQueryInfoFlags         flags,
+                            int                         io_priority,
+                            GCancellable               *cancellable,
+                            GAsyncReadyCallback         callback,
+                            gpointer                    user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (G_IS_FILE_INFO (info));
+
+  iface = G_FILE_GET_IFACE (file);
+  (* iface->set_attributes_async) (file, info, flags, io_priority, cancellable, callback, user_data);
+  
+}
+
+/**
+ * g_file_set_attributes_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @info: a #GFileInfo.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the attributes were set correctly, %FALSE otherwise.
+ **/
+gboolean
+g_file_set_attributes_finish (GFile                      *file,
+                             GAsyncResult               *result,
+                             GFileInfo                 **info,
+                             GError                    **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  /* No standard handling of errors here, as we must set info even
+     on errors */
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->set_attributes_finish) (file, result, info, error);
+}
+
+/**
+ * g_file_set_attribute_string:
+ * @file: input #GFile.
+ * @attribute: a string containing the attribute's name.
+ * @value: a string containing the attribute's value.
+ * @flags: #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @attribute was successfully set to @value 
+ * in the @file, %FALSE otherwise.
+ **/
+gboolean
+g_file_set_attribute_string (GFile                  *file,
+                            const char             *attribute,
+                            const char             *value,
+                            GFileQueryInfoFlags       flags,
+                            GCancellable           *cancellable,
+                            GError                **error)
+{
+  GFileAttributeValue v;
+
+  v.type = G_FILE_ATTRIBUTE_TYPE_STRING;
+  v.u.string = (char *)value;
+  return g_file_set_attribute (file, attribute, &v, flags, cancellable, error);
+}
+
+/**
+ * g_file_attribute_byte_string:
+ * @file: input #GFile.
+ * @attribute: a string containing the attribute's name.
+ * @value: a string containing the attribute's new value.
+ * @flags: a #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @attribute was successfully set to @value 
+ * in the @file, %FALSE otherwise.
+ **/
+gboolean
+g_file_set_attribute_byte_string  (GFile                  *file,
+                                  const char             *attribute,
+                                  const char             *value,
+                                  GFileQueryInfoFlags       flags,
+                                  GCancellable           *cancellable,
+                                  GError                **error)
+{
+  GFileAttributeValue v;
+
+  v.type = G_FILE_ATTRIBUTE_TYPE_BYTE_STRING;
+  v.u.string = (char *)value;
+  return g_file_set_attribute (file, attribute, &v, flags, cancellable, error);
+}
+
+/**
+ * g_file_set_attribute_uint32:
+ * @file: input #GFile.
+ * @attribute: a string containing the attribute's name.
+ * @value: a #guint32 containing the attribute's new value.
+ * @flags: a #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @attribute was successfully set to @value 
+ * in the @file, %FALSE otherwise.
+ **/
+gboolean
+g_file_set_attribute_uint32 (GFile                  *file,
+                            const char             *attribute,
+                            guint32                 value,
+                            GFileQueryInfoFlags       flags,
+                            GCancellable           *cancellable,
+                            GError                **error)
+{
+  GFileAttributeValue v;
+
+  v.type = G_FILE_ATTRIBUTE_TYPE_UINT32;
+  v.u.uint32 = value;
+  return g_file_set_attribute (file, attribute, &v, flags, cancellable, error);
+}
+
+/**
+ * g_file_set_attribute_int32:
+ * @file: input #GFile.
+ * @attribute: a string containing the attribute's name.
+ * @value: a #gint32 containing the attribute's new value.
+ * @flags: a #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @attribute was successfully set to @value 
+ * in the @file, %FALSE otherwise. 
+ **/
+gboolean
+g_file_set_attribute_int32 (GFile                  *file,
+                           const char             *attribute,
+                           gint32                  value,
+                           GFileQueryInfoFlags       flags,
+                           GCancellable           *cancellable,
+                           GError                **error)
+{
+  GFileAttributeValue v;
+
+  v.type = G_FILE_ATTRIBUTE_TYPE_INT32;
+  v.u.int32 = value;
+  return g_file_set_attribute (file, attribute, &v, flags, cancellable, error);
+}
+
+/**
+ * g_file_set_attribute_uint64:
+ * @file: input #GFile. 
+ * @attribute: a string containing the attribute's name.
+ * @value: a #guint64 containing the attribute's new value.
+ * @flags: a #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @attribute was successfully set to @value 
+ * in the @file, %FALSE otherwise.
+ **/
+gboolean
+g_file_set_attribute_uint64 (GFile                  *file,
+                            const char             *attribute,
+                            guint64                 value,
+                            GFileQueryInfoFlags       flags,
+                            GCancellable           *cancellable,
+                            GError                **error)
+ {
+  GFileAttributeValue v;
+
+  v.type = G_FILE_ATTRIBUTE_TYPE_UINT64;
+  v.u.uint64 = value;
+  return g_file_set_attribute (file, attribute, &v, flags, cancellable, error);
+}
+
+/**
+ * g_file_set_attribute_int64:
+ * @file: input #GFile.
+ * @attribute: a string containing the attribute's name.
+ * @value: a #guint64 containing the attribute's new value.
+ * @flags: a #GFileQueryInfoFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @attribute was successfully set to @value 
+ * in the @file, %FALSE otherwise.
+ **/
+gboolean
+g_file_set_attribute_int64 (GFile                  *file,
+                           const char             *attribute,
+                           gint64                  value,
+                           GFileQueryInfoFlags       flags,
+                           GCancellable           *cancellable,
+                           GError                **error)
+{
+ GFileAttributeValue v;
+
+  v.type = G_FILE_ATTRIBUTE_TYPE_INT64;
+  v.u.int64 = value;
+  return g_file_set_attribute (file, attribute, &v, flags, cancellable, error);
+}
+
+/**
+ * g_file_mount_mountable:
+ * @file: input #GFile.
+ * @mount_operation: a #GMountOperation.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Mounts a mountable file using @mount_operation, if possible. 
+ *
+ **/
+void
+g_file_mount_mountable (GFile                  *file,
+                       GMountOperation        *mount_operation,
+                       GCancellable           *cancellable,
+                       GAsyncReadyCallback     callback,
+                       gpointer                user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->mount_mountable == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+    }
+  
+  (* iface->mount_mountable) (file,
+                             mount_operation,
+                             cancellable,
+                             callback,
+                             user_data);
+  
+}
+
+/**
+ * g_file_mount_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError. 
+ * 
+ * Returns: a #GFile or %NULL on error.
+ **/
+GFile *
+g_file_mount_mountable_finish (GFile                  *file,
+                              GAsyncResult           *result,
+                              GError                **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->mount_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_unmount_mountable:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ **/
+void
+g_file_unmount_mountable (GFile                  *file,
+                         GCancellable           *cancellable,
+                         GAsyncReadyCallback     callback,
+                         gpointer                user_data)
+{
+  GFileIface *iface;
+  
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  
+  if (iface->unmount_mountable == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+    }
+  
+  (* iface->unmount_mountable) (file,
+                               cancellable,
+                               callback,
+                               user_data);
+}
+
+/**
+ * g_file_unmount_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the operation finished successfully. %FALSE
+ * otherwise.
+ **/
+gboolean
+g_file_unmount_mountable_finish   (GFile                  *file,
+                                  GAsyncResult           *result,
+                                  GError                **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->unmount_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_eject_mountable:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ **/
+void
+g_file_eject_mountable (GFile                  *file,
+                       GCancellable           *cancellable,
+                       GAsyncReadyCallback     callback,
+                       gpointer                user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  iface = G_FILE_GET_IFACE (file);
+  
+  if (iface->eject_mountable == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (file),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR,
+                                          G_IO_ERROR_NOT_SUPPORTED,
+                                          _("Operation not supported"));
+    }
+  
+  (* iface->eject_mountable) (file,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+/**
+ * g_file_eject_mountable_finish:
+ * @file: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @file was ejected successfully. %FALSE 
+ * otherwise.
+ **/
+gboolean
+g_file_eject_mountable_finish (GFile                  *file,
+                              GAsyncResult           *result,
+                              GError                **error)
+{
+  GFileIface *iface;
+  
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_FILE_GET_IFACE (file);
+  return (* iface->eject_mountable_finish) (file, result, error);
+}
+
+/**
+ * g_file_monitor_directory:
+ * @file: input #GFile.
+ * @flags: a set of #GFileMonitorFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * 
+ * Returns: a #GDirectoryMonitor for the given @file, 
+ * or %NULL on error.
+ **/
+GDirectoryMonitor*
+g_file_monitor_directory (GFile *file,
+                         GFileMonitorFlags flags,
+                         GCancellable *cancellable)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  if (iface->monitor_dir == NULL)
+    return NULL;
+
+  return (* iface->monitor_dir) (file, flags, cancellable);
+}
+
+/**
+ * g_file_monitor_file:
+ * @file: input #GFile.
+ * @flags: a set of #GFileMonitorFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * 
+ * Returns: a #GFileMonitor for the given @file, 
+ * or %NULL on error.
+ **/
+GFileMonitor*
+g_file_monitor_file (GFile *file,
+                    GFileMonitorFlags flags,
+                    GCancellable *cancellable)
+{
+  GFileIface *iface;
+  GFileMonitor *monitor;
+  
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  iface = G_FILE_GET_IFACE (file);
+
+  monitor = NULL;
+  
+  if (iface->monitor_file)
+    monitor = (* iface->monitor_file) (file, flags, cancellable);
+
+/* Fallback to polling */
+  if (monitor == NULL)
+    monitor = g_poll_file_monitor_new (file);
+
+  return monitor;
+}
+
+/********************************************
+ *   Default implementation of async ops    *
+ ********************************************/
+
+typedef struct {
+  char *attributes;
+  GFileQueryInfoFlags flags;
+  GFileInfo *info;
+} QueryInfoAsyncData;
+
+static void
+query_info_data_free (QueryInfoAsyncData *data)
+{
+  if (data->info)
+    g_object_unref (data->info);
+  g_free (data->attributes);
+  g_free (data);
+}
+
+static void
+query_info_async_thread (GSimpleAsyncResult *res,
+                        GObject *object,
+                        GCancellable *cancellable)
+{
+  GError *error = NULL;
+  QueryInfoAsyncData *data;
+  GFileInfo *info;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+  
+  info = g_file_query_info (G_FILE (object), data->attributes, data->flags, cancellable, &error);
+
+  if (info == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->info = info;
+}
+
+static void
+g_file_real_query_info_async (GFile                      *file,
+                             const char                 *attributes,
+                             GFileQueryInfoFlags         flags,
+                             int                         io_priority,
+                             GCancellable               *cancellable,
+                             GAsyncReadyCallback         callback,
+                             gpointer                    user_data)
+{
+  GSimpleAsyncResult *res;
+  QueryInfoAsyncData *data;
+
+  data = g_new0 (QueryInfoAsyncData, 1);
+  data->attributes = g_strdup (attributes);
+  data->flags = flags;
+  
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_query_info_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free);
+  
+  g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileInfo *
+g_file_real_query_info_finish (GFile                      *file,
+                              GAsyncResult               *res,
+                              GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  QueryInfoAsyncData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_query_info_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->info)
+    return g_object_ref (data->info);
+  
+  return NULL;
+}
+
+typedef struct {
+  char *attributes;
+  GFileQueryInfoFlags flags;
+  GFileEnumerator *enumerator;
+} EnumerateChildrenAsyncData;
+
+static void
+enumerate_children_data_free (EnumerateChildrenAsyncData *data)
+{
+  if (data->enumerator)
+    g_object_unref (data->enumerator);
+  g_free (data->attributes);
+  g_free (data);
+}
+
+static void
+enumerate_children_async_thread (GSimpleAsyncResult *res,
+                                GObject *object,
+                                GCancellable *cancellable)
+{
+  GError *error = NULL;
+  EnumerateChildrenAsyncData *data;
+  GFileEnumerator *enumerator;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+  
+  enumerator = g_file_enumerate_children (G_FILE (object), data->attributes, data->flags, cancellable, &error);
+
+  if (enumerator == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->enumerator = enumerator;
+}
+
+static void
+g_file_real_enumerate_children_async (GFile                      *file,
+                                     const char                 *attributes,
+                                     GFileQueryInfoFlags         flags,
+                                     int                         io_priority,
+                                     GCancellable               *cancellable,
+                                     GAsyncReadyCallback         callback,
+                                     gpointer                    user_data)
+{
+  GSimpleAsyncResult *res;
+  EnumerateChildrenAsyncData *data;
+
+  data = g_new0 (EnumerateChildrenAsyncData, 1);
+  data->attributes = g_strdup (attributes);
+  data->flags = flags;
+  
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_enumerate_children_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)enumerate_children_data_free);
+  
+  g_simple_async_result_run_in_thread (res, enumerate_children_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileEnumerator *
+g_file_real_enumerate_children_finish (GFile                      *file,
+                                      GAsyncResult               *res,
+                                      GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  EnumerateChildrenAsyncData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_enumerate_children_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->enumerator)
+    return g_object_ref (data->enumerator);
+  
+  return NULL;
+}
+
+static void
+open_read_async_thread (GSimpleAsyncResult *res,
+                       GObject *object,
+                       GCancellable *cancellable)
+{
+  GFileIface *iface;
+  GFileInputStream *stream;
+  GError *error = NULL;
+
+  iface = G_FILE_GET_IFACE (object);
+
+  stream = iface->read (G_FILE (object), cancellable, &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+}
+
+static void
+g_file_real_read_async (GFile                  *file,
+                       int                     io_priority,
+                       GCancellable           *cancellable,
+                       GAsyncReadyCallback     callback,
+                       gpointer                user_data)
+{
+  GSimpleAsyncResult *res;
+  
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_read_async);
+  
+  g_simple_async_result_run_in_thread (res, open_read_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileInputStream *
+g_file_real_read_finish (GFile                  *file,
+                        GAsyncResult           *res,
+                        GError                **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_read_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+  
+  return NULL;
+}
+
+static void
+append_to_async_thread (GSimpleAsyncResult *res,
+                       GObject *object,
+                       GCancellable *cancellable)
+{
+  GFileIface *iface;
+  GFileCreateFlags *data;
+  GFileOutputStream *stream;
+  GError *error = NULL;
+
+  iface = G_FILE_GET_IFACE (object);
+
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  stream = iface->append_to (G_FILE (object), *data, cancellable, &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+}
+
+static void
+g_file_real_append_to_async (GFile                  *file,
+                            GFileCreateFlags        flags,
+                            int                     io_priority,
+                            GCancellable           *cancellable,
+                            GAsyncReadyCallback     callback,
+                            gpointer                user_data)
+{
+  GFileCreateFlags *data;
+  GSimpleAsyncResult *res;
+
+  data = g_new0 (GFileCreateFlags, 1);
+  *data = flags;
+
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_append_to_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)g_free);
+
+  g_simple_async_result_run_in_thread (res, append_to_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileOutputStream *
+g_file_real_append_to_finish (GFile                  *file,
+                             GAsyncResult           *res,
+                             GError                **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_append_to_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+  
+  return NULL;
+}
+
+static void
+create_async_thread (GSimpleAsyncResult *res,
+                    GObject *object,
+                    GCancellable *cancellable)
+{
+  GFileIface *iface;
+  GFileCreateFlags *data;
+  GFileOutputStream *stream;
+  GError *error = NULL;
+
+  iface = G_FILE_GET_IFACE (object);
+
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  stream = iface->create (G_FILE (object), *data, cancellable, &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    g_simple_async_result_set_op_res_gpointer (res, stream, g_object_unref);
+}
+
+static void
+g_file_real_create_async (GFile                  *file,
+                         GFileCreateFlags        flags,
+                         int                     io_priority,
+                         GCancellable           *cancellable,
+                         GAsyncReadyCallback     callback,
+                         gpointer                user_data)
+{
+  GFileCreateFlags *data;
+  GSimpleAsyncResult *res;
+
+  data = g_new0 (GFileCreateFlags, 1);
+  *data = flags;
+
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_create_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)g_free);
+
+  g_simple_async_result_run_in_thread (res, create_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileOutputStream *
+g_file_real_create_finish (GFile                  *file,
+                          GAsyncResult           *res,
+                          GError                **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_create_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+  
+  return NULL;
+}
+
+typedef struct {
+  GFileOutputStream *stream;
+  char *etag;
+  gboolean make_backup;
+  GFileCreateFlags flags;
+} ReplaceAsyncData;
+
+static void
+replace_async_data_free (ReplaceAsyncData *data)
+{
+  if (data->stream)
+    g_object_unref (data->stream);
+  g_free (data->etag);
+  g_free (data);
+}
+
+static void
+replace_async_thread (GSimpleAsyncResult *res,
+                     GObject *object,
+                     GCancellable *cancellable)
+{
+  GFileIface *iface;
+  GFileOutputStream *stream;
+  GError *error = NULL;
+  ReplaceAsyncData *data;
+
+  iface = G_FILE_GET_IFACE (object);
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  stream = iface->replace (G_FILE (object),
+                          data->etag,
+                          data->make_backup,
+                          data->flags,
+                          cancellable,
+                          &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->stream = stream;
+}
+
+static void
+g_file_real_replace_async (GFile                  *file,
+                          const char             *etag,
+                          gboolean                make_backup,
+                          GFileCreateFlags        flags,
+                          int                     io_priority,
+                          GCancellable           *cancellable,
+                          GAsyncReadyCallback     callback,
+                          gpointer                user_data)
+{
+  GSimpleAsyncResult *res;
+  ReplaceAsyncData *data;
+
+  data = g_new0 (ReplaceAsyncData, 1);
+  data->etag = g_strdup (etag);
+  data->make_backup = make_backup;
+  data->flags = flags;
+
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_replace_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)replace_async_data_free);
+
+  g_simple_async_result_run_in_thread (res, replace_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileOutputStream *
+g_file_real_replace_finish (GFile                  *file,
+                           GAsyncResult           *res,
+                           GError                **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  ReplaceAsyncData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_replace_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->stream)
+    return g_object_ref (data->stream);
+  
+  return NULL;
+}
+
+typedef struct {
+  char *name;
+  GFile *file;
+} SetDisplayNameAsyncData;
+
+static void
+set_display_name_data_free (SetDisplayNameAsyncData *data)
+{
+  g_free (data->name);
+  if (data->file)
+    g_object_unref (data->file);
+  g_free (data);
+}
+
+static void
+set_display_name_async_thread (GSimpleAsyncResult *res,
+                              GObject *object,
+                              GCancellable *cancellable)
+{
+  GError *error = NULL;
+  SetDisplayNameAsyncData *data;
+  GFile *file;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+  
+  file = g_file_set_display_name (G_FILE (object), data->name, cancellable, &error);
+
+  if (file == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->file = file;
+}
+
+static void
+g_file_real_set_display_name_async (GFile                      *file,  
+                                   const char                 *display_name,
+                                   int                         io_priority,
+                                   GCancellable               *cancellable,
+                                   GAsyncReadyCallback         callback,
+                                   gpointer                    user_data)
+{
+  GSimpleAsyncResult *res;
+  SetDisplayNameAsyncData *data;
+
+  data = g_new0 (SetDisplayNameAsyncData, 1);
+  data->name = g_strdup (display_name);
+  
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_set_display_name_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)set_display_name_data_free);
+  
+  g_simple_async_result_run_in_thread (res, set_display_name_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFile *
+g_file_real_set_display_name_finish (GFile                      *file,
+                                    GAsyncResult               *res,
+                                    GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  SetDisplayNameAsyncData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_set_display_name_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->file)
+    return g_object_ref (data->file);
+  
+  return NULL;
+}
+
+typedef struct {
+  GFileQueryInfoFlags flags;
+  GFileInfo *info;
+  gboolean res;
+  GError *error;
+} SetInfoAsyncData;
+
+static void
+set_info_data_free (SetInfoAsyncData *data)
+{
+  if (data->info)
+    g_object_unref (data->info);
+  if (data->error)
+    g_error_free (data->error);
+  g_free (data);
+}
+
+static void
+set_info_async_thread (GSimpleAsyncResult *res,
+                      GObject *object,
+                      GCancellable *cancellable)
+{
+  SetInfoAsyncData *data;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+  
+  data->error = NULL;
+  data->res = g_file_set_attributes_from_info (G_FILE (object),
+                                              data->info,
+                                              data->flags,
+                                              cancellable,
+                                              &data->error);
+}
+
+static void
+g_file_real_set_attributes_async (GFile                      *file,
+                                 GFileInfo                  *info,
+                                 GFileQueryInfoFlags        flags,
+                                 int                         io_priority,
+                                 GCancellable               *cancellable,
+                                 GAsyncReadyCallback         callback,
+                                 gpointer                    user_data)
+{
+  GSimpleAsyncResult *res;
+  SetInfoAsyncData *data;
+
+  data = g_new0 (SetInfoAsyncData, 1);
+  data->info = g_file_info_dup (info);
+  data->flags = flags;
+  
+  res = g_simple_async_result_new (G_OBJECT (file), callback, user_data, g_file_real_set_attributes_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)set_info_data_free);
+  
+  g_simple_async_result_run_in_thread (res, set_info_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_file_real_set_attributes_finish (GFile                      *file,
+                                  GAsyncResult               *res,
+                                  GFileInfo                 **info,
+                                  GError                    **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  SetInfoAsyncData *data;
+  
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_real_set_attributes_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (info) 
+    *info = g_object_ref (data->info);
+
+  if (error != NULL && data->error) {
+    *error = g_error_copy (data->error);
+  }
+  
+  return data->res;
+}
+
+/********************************************
+ *   Default VFS operations                 *
+ ********************************************/
+
+/**
+ * g_file_new_for_path:
+ * @path: a string containing a relative or absolute path.
+ * 
+ * Constructs a #GFile for given @path. This operation never
+ * fails, but the returned object might not support any I/O
+ * operation if the @path is malformed.
+ * 
+ * Returns a new #GFile for the given @path. 
+ **/
+GFile *
+g_file_new_for_path (const char *path)
+{
+  g_return_val_if_fail (path != NULL, NULL);
+
+  return g_vfs_get_file_for_path (g_vfs_get_default (),
+                                 path);
+}
+/**
+ * g_file_new_for_uri:
+ * @uri: a string containing a URI.
+ * 
+ * This operation never fails, but the returned object
+ * might not support any I/O operation if the @uri
+ * is malformed or if the uri type is not supported.
+ * 
+ * Returns a #GFile for the given @uri.
+ **/ 
+GFile *
+g_file_new_for_uri (const char *uri)
+{
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  return g_vfs_get_file_for_uri (g_vfs_get_default (),
+                                uri);
+}
+  
+/**
+ * g_file_parse_name:
+ * @parse_name: a file name or path to be parsed.
+ * 
+ * Constructs a #GFile with the given @parse_name, 
+ * looked up by #GVfs. This operation never fails, 
+ * but the returned object might not support any I/O
+ * operation if the @parse_name cannot be parsed by #GVfs.
+ * 
+ * Returns a new #GFile.
+ **/
+GFile *
+g_file_parse_name (const char *parse_name)
+{
+  g_return_val_if_fail (parse_name != NULL, NULL);
+
+  return g_vfs_parse_name (g_vfs_get_default (),
+                          parse_name);
+}
+
+static gboolean
+is_valid_scheme_character (char c)
+{
+  return g_ascii_isalnum (c) || c == '+' || c == '-' || c == '.';
+}
+
+static gboolean
+has_valid_scheme (const char *uri)
+{
+  const char *p;
+  
+  p = uri;
+  
+  if (!is_valid_scheme_character (*p))
+    return FALSE;
+
+  do {
+    p++;
+  } while (is_valid_scheme_character (*p));
+
+  return *p == ':';
+}
+
+/**
+ * g_file_new_for_commandline_arg:
+ * @arg: a command line string.
+ * 
+ * Attempts to generate a #GFile with the given line from
+ * the command line argument. 
+ * 
+ * Returns a new #GFile. 
+ **/
+GFile *
+g_file_new_for_commandline_arg (const char *arg)
+{
+  GFile *file;
+  char *filename;
+  char *current_dir;
+  
+  g_return_val_if_fail (arg != NULL, NULL);
+  
+  if (g_path_is_absolute (arg))
+    return g_file_new_for_path (arg);
+
+  if (has_valid_scheme (arg))
+    return g_file_new_for_uri (arg);
+    
+  current_dir = g_get_current_dir ();
+  filename = g_build_filename (current_dir, arg, NULL);
+  g_free (current_dir);
+  
+  file = g_file_new_for_path (filename);
+  g_free (filename);
+  
+  return file;
+}
+
+/**
+ * g_mount_for_location:
+ * @location: input #GFile.
+ * @mount_operation: a #GMountOperation.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Starts the @mount_operation, mounting the volume at @location. This
+ * operation is cancellable with @cancellable. When the operation has 
+ * completed, @callback will be called with user data. To finish 
+ * the operation, call g_mount_for_location_finish() with the 
+ * #GAsyncResult returned by the @callback.
+ * 
+ **/
+void
+g_mount_for_location (GFile                  *location,
+                     GMountOperation        *mount_operation,
+                     GCancellable           *cancellable,
+                     GAsyncReadyCallback     callback,
+                     gpointer                user_data)
+{
+  GFileIface *iface;
+
+  g_return_if_fail (G_IS_FILE (location));
+  g_return_if_fail (G_IS_MOUNT_OPERATION (mount_operation));
+
+  iface = G_FILE_GET_IFACE (location);
+
+  if (iface->mount_for_location == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (location),
+                                          callback, user_data,
+                                          G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                          _("volume doesn't implement mount"));
+      
+      return;
+    }
+  
+  (* iface->mount_for_location) (location, mount_operation, cancellable, callback, user_data);
+
+}
+
+/**
+ * g_mount_for_location_finish:
+ * @location: input #GFile.
+ * @result: a #GAsyncResult.
+ * @error: a #GError. 
+ * 
+ * Finishes an Asynchronous mount operation. 
+ * 
+ * Returns: %TRUE if the mount was finished successfully. If %FALSE and 
+ * @error is present, it will be set appropriately. 
+ **/
+gboolean
+g_mount_for_location_finish (GFile                  *location,
+                            GAsyncResult           *result,
+                            GError                **error)
+{
+  GFileIface *iface;
+
+  g_return_val_if_fail (G_IS_FILE (location), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_FILE_GET_IFACE (location);
+
+  return (* iface->mount_for_location_finish) (location, result, error);
+}
+
+/********************************************
+ *   Utility functions                      *
+ ********************************************/
+
+#define GET_CONTENT_BLOCK_SIZE 8192
+
+/**
+ * g_file_load_contents:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @contents: 
+ * @length:
+ * @etag_out: a pointer to the current entity tag for the document.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the @file's contents were successfully loaded.
+ * %FALSE if there were errors. The length of the loaded data will be
+ * put into @length, the contents in @contents.
+ **/
+gboolean
+g_file_load_contents (GFile                *file,
+                     GCancellable         *cancellable,
+                     char                **contents,
+                     gsize                *length,
+                     char                **etag_out,
+                     GError              **error)
+{
+  GFileInputStream *in;
+  GByteArray *content;
+  gsize pos;
+  gssize res;
+  GFileInfo *info;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (contents != NULL, FALSE);
+
+  in = g_file_read (file,
+                   cancellable,
+                   error);
+  if (in == NULL)
+    return FALSE;
+
+  content = g_byte_array_new ();
+  pos = 0;
+  
+  g_byte_array_set_size (content, pos + GET_CONTENT_BLOCK_SIZE + 1);
+  while ((res = g_input_stream_read (G_INPUT_STREAM (in),
+                                    content->data + pos,
+                                    GET_CONTENT_BLOCK_SIZE,
+                                    cancellable, error)) > 0)
+    {
+      pos += res;
+      g_byte_array_set_size (content, pos + GET_CONTENT_BLOCK_SIZE + 1);
+    }
+
+  if (etag_out)
+    {
+      *etag_out = NULL;
+      
+      info = g_file_input_stream_query_info (in,
+                                            G_FILE_ATTRIBUTE_ETAG_VALUE,
+                                            cancellable,
+                                            NULL);
+      if (info)
+       {
+         *etag_out = g_strdup (g_file_info_get_etag (info));
+         g_object_unref (info);
+       }
+    } 
+
+  /* Ignore errors on close */
+  g_input_stream_close (G_INPUT_STREAM (in), cancellable, NULL);
+  g_object_unref (in);
+
+  if (res < 0)
+    {
+      /* error is set already */
+      g_byte_array_free (content, TRUE);
+      return FALSE;
+    }
+
+  if (length)
+    *length = pos;
+
+  /* Zero terminate (we got an extra byte allocated for this */
+  content->data[pos] = 0;
+  
+  *contents = (char *)g_byte_array_free (content, FALSE);
+  
+  return TRUE;
+}
+
+typedef struct {
+  GFile *file;
+  GError *error;
+  GCancellable *cancellable;
+  GFileReadMoreCallback read_more_callback;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GByteArray *content;
+  gsize pos;
+  char *etag;
+} LoadContentsData;
+
+
+static void
+load_contents_data_free (LoadContentsData *data)
+{
+  if (data->error)
+    g_error_free (data->error);
+  if (data->cancellable)
+    g_object_unref (data->cancellable);
+  if (data->content)
+    g_byte_array_free (data->content, TRUE);
+  g_free (data->etag);
+  g_object_unref (data->file);
+  g_free (data);
+}
+
+static void
+load_contents_close_callback (GObject *obj,
+                            GAsyncResult *close_res,
+                            gpointer user_data)
+{
+  GInputStream *stream = G_INPUT_STREAM (obj);
+  LoadContentsData *data = user_data;
+  GSimpleAsyncResult *res;
+
+  /* Ignore errors here, we're only reading anyway */
+  g_input_stream_close_finish (stream, close_res, NULL);
+  g_object_unref (stream);
+
+  res = g_simple_async_result_new (G_OBJECT (data->file),
+                                  data->callback,
+                                  data->user_data,
+                                  g_file_load_contents_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)load_contents_data_free);
+  g_simple_async_result_complete (res);
+  g_object_unref (res);
+}
+
+static void
+load_contents_fstat_callback (GObject *obj,
+                             GAsyncResult *stat_res,
+                             gpointer user_data)
+{
+  GInputStream *stream = G_INPUT_STREAM (obj);
+  LoadContentsData *data = user_data;
+  GFileInfo *info;
+
+  info = g_file_input_stream_query_info_finish (G_FILE_INPUT_STREAM (stream),
+                                                  stat_res, NULL);
+  if (info)
+    {
+      data->etag = g_strdup (g_file_info_get_etag (info));
+      g_object_unref (info);
+    }
+
+  g_input_stream_close_async (stream, 0,
+                             data->cancellable,
+                             load_contents_close_callback, data);
+}
+
+static void
+load_contents_read_callback (GObject *obj,
+                            GAsyncResult *read_res,
+                            gpointer user_data)
+{
+  GInputStream *stream = G_INPUT_STREAM (obj);
+  LoadContentsData *data = user_data;
+  GError *error = NULL;
+  gssize read_size;
+
+  read_size = g_input_stream_read_finish (stream, read_res, &error);
+
+  if (read_size < 0) 
+    {
+      /* Error or EOF, close the file */
+      data->error = error;
+      g_input_stream_close_async (stream, 0,
+                                 data->cancellable,
+                                 load_contents_close_callback, data);
+    }
+  else if (read_size == 0)
+    {
+      g_file_input_stream_query_info_async (G_FILE_INPUT_STREAM (stream),
+                                           G_FILE_ATTRIBUTE_ETAG_VALUE,
+                                           0,
+                                           data->cancellable,
+                                           load_contents_fstat_callback,
+                                           data);
+    }
+  else if (read_size > 0)
+    {
+      data->pos += read_size;
+      
+      g_byte_array_set_size (data->content,
+                            data->pos + GET_CONTENT_BLOCK_SIZE);
+
+
+      if (data->read_more_callback &&
+         !data->read_more_callback ((char *)data->content->data, data->pos, data->user_data))
+       g_file_input_stream_query_info_async (G_FILE_INPUT_STREAM (stream),
+                                             G_FILE_ATTRIBUTE_ETAG_VALUE,
+                                             0,
+                                             data->cancellable,
+                                             load_contents_fstat_callback,
+                                             data);
+      else 
+       g_input_stream_read_async (stream,
+                                  data->content->data + data->pos,
+                                  GET_CONTENT_BLOCK_SIZE,
+                                  0,
+                                  data->cancellable,
+                                  load_contents_read_callback,
+                                  data);
+    }
+}
+
+static void
+load_contents_open_callback (GObject *obj,
+                            GAsyncResult *open_res,
+                            gpointer user_data)
+{
+  GFile *file = G_FILE (obj);
+  GFileInputStream *stream;
+  LoadContentsData *data = user_data;
+  GError *error = NULL;
+  GSimpleAsyncResult *res;
+
+  stream = g_file_read_finish (file, open_res, &error);
+
+  if (stream)
+    {
+      g_byte_array_set_size (data->content,
+                            data->pos + GET_CONTENT_BLOCK_SIZE);
+      g_input_stream_read_async (G_INPUT_STREAM (stream),
+                                data->content->data + data->pos,
+                                GET_CONTENT_BLOCK_SIZE,
+                                0,
+                                data->cancellable,
+                                load_contents_read_callback,
+                                data);
+      
+    }
+  else
+    {
+      res = g_simple_async_result_new_from_error (G_OBJECT (data->file),
+                                                 data->callback,
+                                                 data->user_data,
+                                                 error);
+      g_simple_async_result_complete (res);
+      g_error_free (error);
+      load_contents_data_free (data);
+      g_object_unref (res);
+    }
+}
+
+/**
+ * g_file_load_partial_contents_async:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @read_more_callback: a #GFileReadMoreCallback.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_load_partial_contents_async (GFile                *file,
+                                   GCancellable         *cancellable,
+                                   GFileReadMoreCallback read_more_callback,
+                                   GAsyncReadyCallback   callback,
+                                   gpointer              user_data)
+{
+  LoadContentsData *data;
+
+  g_return_if_fail (G_IS_FILE (file));
+
+  data = g_new0 (LoadContentsData, 1);
+
+  if (cancellable)
+    data->cancellable = g_object_ref (cancellable);
+  data->read_more_callback = read_more_callback;
+  data->callback = callback;
+  data->user_data = user_data;
+  data->content = g_byte_array_new ();
+  data->file = g_object_ref (file);
+
+  g_file_read_async (file,
+                    0,
+                    cancellable,
+                    load_contents_open_callback,
+                    data);
+}
+
+/**
+ * g_file_load_partial_contents_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @contents: a pointer to where the contents of the file will be placed.
+ * @length: a pointer to the location where the content's length will be placed.
+ * @etag_out: a pointer to the current entity tag for the document.
+ * @error: a #GError. 
+ * 
+ * Returns: %TRUE if the load was successful. If %FALSE and @error is 
+ * present, it will be set appropriately. 
+ **/
+gboolean
+g_file_load_partial_contents_finish (GFile                *file,
+                                    GAsyncResult         *res,
+                                    char                **contents,
+                                    gsize                *length,
+                                    char                **etag_out,
+                                    GError              **error)
+{
+  GSimpleAsyncResult *simple;
+  LoadContentsData *data;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE);
+  g_return_val_if_fail (contents != NULL, FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+  
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_load_contents_async);
+  
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (data->error)
+    {
+      g_propagate_error (error, data->error);
+      data->error = NULL;
+      *contents = NULL;
+      if (length)
+       *length = 0;
+      return FALSE;
+    }
+
+  if (length)
+    *length = data->pos;
+
+  if (etag_out)
+    {
+      *etag_out = data->etag;
+      data->etag = NULL;
+    }
+
+  /* Zero terminate */
+  g_byte_array_set_size (data->content,
+                        data->pos + 1);
+  data->content->data[data->pos] = 0;
+  
+  *contents = (char *)g_byte_array_free (data->content, FALSE);
+  data->content = NULL;
+
+  return TRUE;
+}
+
+/**
+ * g_file_load_contents_async:
+ * @file: input #GFile.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Starts an asynchronous load of the @file's contents. When the load operation 
+ * has completed, @callback will be called with @userdata. To finish 
+ * the operation, call g_file_load_contents_finish() with the 
+ * #GAsyncResult returned by the @callback.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ **/
+void
+g_file_load_contents_async (GFile                *file,
+                          GCancellable         *cancellable,
+                          GAsyncReadyCallback   callback,
+                          gpointer              user_data)
+{
+  g_file_load_partial_contents_async (file,
+                                     cancellable,
+                                     NULL,
+                                     callback, user_data);
+  
+}
+
+/**
+ * g_file_load_contents_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @contents: an array of strings.
+ * @length: a pointer to a #gsize.
+ * @etag_out: a pointer to a string to get the new entity tag.
+ * @error: a #GError. 
+ * 
+ * Finishes an asynchronous load of the @file's contents. The contents 
+ * are placed in @contents, and @length is set to the size of the @contents 
+ * string. If @etag_out is present, it will be set to the new entity 
+ * tag for the @file.
+ * 
+ * Returns: %TRUE if the load was successful. If %FALSE and @error is 
+ * present, it will be set appropriately. 
+ **/
+gboolean
+g_file_load_contents_finish (GFile                *file,
+                            GAsyncResult         *res,
+                            char                **contents,
+                            gsize                *length,
+                            char                **etag_out,
+                            GError              **error)
+{
+  return g_file_load_partial_contents_finish (file,
+                                             res,
+                                             contents,
+                                             length,
+                                             etag_out,
+                                             error);
+  
+}
+  
+/**
+ * g_file_replace_contents:
+ * @file: input #GFile.
+ * @length: a #gsize.
+ * @etag: the old entity tag for the document.
+ * @make_backup: a #gboolean.
+ * @flags: a set of #GFileCreateFlags.
+ * @new_etag: a new entity tag for the document.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: a #GError. 
+ *
+ * Replaces the contents of @file with @contents of @length. The old 
+ * @etag will be replaced with the @new_etag. If @make_backup is %TRUE, 
+ * this function will attempt to make a backup of @file.
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * Returns: %TRUE if successful. If an error
+ * has occured, this function will return %FALSE and set @error
+ * appropriately if present.
+ **/
+gboolean
+g_file_replace_contents (GFile                *file,
+                        const char           *contents,
+                        gsize                 length,
+                        const char           *etag,
+                        gboolean              make_backup,
+                        GFileCreateFlags      flags,
+                        char                **new_etag,
+                        GCancellable         *cancellable,
+                        GError              **error)
+{
+  GFileOutputStream *out;
+  gsize pos, remainder;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (contents != NULL, FALSE);
+
+  out = g_file_replace (file,
+                       etag,
+                       make_backup,
+                       flags,
+                       cancellable,
+                       error);
+  if (out == NULL)
+    return FALSE;
+
+  pos = 0;
+  remainder = length;
+  while (remainder > 0 &&
+        (res = g_output_stream_write (G_OUTPUT_STREAM (out),
+                                      contents + pos,
+                                      MIN (remainder, GET_CONTENT_BLOCK_SIZE),
+                                      cancellable,
+                                      error)) > 0)
+    {
+      pos += res;
+      remainder -= res;
+    }
+  
+  if (remainder > 0 && res < 0)
+    {
+      /* Ignore errors on close */
+      g_output_stream_close (G_OUTPUT_STREAM (out), cancellable, NULL);
+      
+      /* error is set already */
+      return FALSE;
+    }
+  
+  if (!g_output_stream_close (G_OUTPUT_STREAM (out), cancellable, error))
+    return FALSE;
+
+  if (new_etag)
+    *new_etag = g_file_output_stream_get_etag (out);
+  
+  return TRUE;
+}
+
+typedef struct {
+  GFile *file;
+  GError *error;
+  GCancellable *cancellable;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  const char *content;
+  gsize length;
+  gsize pos;
+  char *etag;
+} ReplaceContentsData;
+
+static void
+replace_contents_data_free (ReplaceContentsData *data)
+{
+  if (data->error)
+    g_error_free (data->error);
+  if (data->cancellable)
+    g_object_unref (data->cancellable);
+  g_object_unref (data->file);
+  g_free (data->etag);
+  g_free (data);
+}
+
+static void
+replace_contents_close_callback (GObject *obj,
+                                GAsyncResult *close_res,
+                                gpointer user_data)
+{
+  GOutputStream *stream = G_OUTPUT_STREAM (obj);
+  ReplaceContentsData *data = user_data;
+  GSimpleAsyncResult *res;
+
+  /* Ignore errors here, we're only reading anyway */
+  g_output_stream_close_finish (stream, close_res, NULL);
+  g_object_unref (stream);
+
+  data->etag = g_file_output_stream_get_etag (G_FILE_OUTPUT_STREAM (stream));
+  
+  res = g_simple_async_result_new (G_OBJECT (data->file),
+                                  data->callback,
+                                  data->user_data,
+                                  g_file_replace_contents_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)replace_contents_data_free);
+  g_simple_async_result_complete (res);
+  g_object_unref (res);
+}
+
+static void
+replace_contents_write_callback (GObject *obj,
+                                GAsyncResult *read_res,
+                                gpointer user_data)
+{
+  GOutputStream *stream = G_OUTPUT_STREAM (obj);
+  ReplaceContentsData *data = user_data;
+  GError *error = NULL;
+  gssize write_size;
+  
+  write_size = g_output_stream_write_finish (stream, read_res, &error);
+
+  if (write_size <= 0) 
+    {
+      /* Error or EOF, close the file */
+      if (write_size < 0)
+       data->error = error;
+      g_output_stream_close_async (stream, 0,
+                                  data->cancellable,
+                                  replace_contents_close_callback, data);
+    }
+  else if (write_size > 0)
+    {
+      data->pos += write_size;
+
+      if (data->pos >= data->length)
+       g_output_stream_close_async (stream, 0,
+                                    data->cancellable,
+                                    replace_contents_close_callback, data);
+      else
+       g_output_stream_write_async (stream,
+                                    data->content + data->pos,
+                                    data->length - data->pos,
+                                    0,
+                                    data->cancellable,
+                                    replace_contents_write_callback,
+                                    data);
+    }
+}
+
+static void
+replace_contents_open_callback (GObject *obj,
+                               GAsyncResult *open_res,
+                               gpointer user_data)
+{
+  GFile *file = G_FILE (obj);
+  GFileOutputStream *stream;
+  ReplaceContentsData *data = user_data;
+  GError *error = NULL;
+  GSimpleAsyncResult *res;
+
+  stream = g_file_replace_finish (file, open_res, &error);
+
+  if (stream)
+    {
+      g_output_stream_write_async (G_OUTPUT_STREAM (stream),
+                                  data->content + data->pos,
+                                  data->length - data->pos,
+                                  0,
+                                  data->cancellable,
+                                  replace_contents_write_callback,
+                                  data);
+      
+    }
+  else
+    {
+      res = g_simple_async_result_new_from_error (G_OBJECT (data->file),
+                                                 data->callback,
+                                                 data->user_data,
+                                                 error);
+      g_simple_async_result_complete (res);
+      g_error_free (error);
+      replace_contents_data_free (data);
+      g_object_unref (res);
+    }
+}
+
+/**
+ * g_file_replace_contents_async:
+ * @file: input #GFile.
+ * @contents: string of contents to replace the file with.
+ * @length: length of the @contents string.
+ * @etag: a new entity tag for the @file.
+ * @make_backup: a #gboolean.
+ * @flags: a set of #GFileCreateFlags.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback. 
+ * @user_data: a #gpointer. 
+ * 
+ * Starts an asynchronous replacement of @file with the given 
+ * @contents of @length bytes. @etag will replace the document's 
+ * current entity tag.
+ * 
+ * When this operation has completed, @callback will be called with
+ * @user_user data, and the operation can be finalized with 
+ * g_file_replace_contents_finish().
+ * 
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ * 
+ * If @make_backup is %TRUE, this function will attempt to 
+ * make a backup of @file.
+ * 
+ **/
+void
+g_file_replace_contents_async  (GFile                *file,
+                               const char           *contents,
+                               gsize                 length,
+                               const char           *etag,
+                               gboolean              make_backup,
+                               GFileCreateFlags      flags,
+                               GCancellable         *cancellable,
+                               GAsyncReadyCallback   callback,
+                               gpointer              user_data)
+{
+  ReplaceContentsData *data;
+
+  g_return_if_fail (G_IS_FILE (file));
+  g_return_if_fail (contents != NULL);
+
+  data = g_new0 (ReplaceContentsData, 1);
+
+  if (cancellable)
+    data->cancellable = g_object_ref (cancellable);
+  data->callback = callback;
+  data->user_data = user_data;
+  data->content = contents;
+  data->length = length;
+  data->pos = 0;
+  data->file = g_object_ref (file);
+
+  g_file_replace_async (file,
+                       etag,
+                       make_backup,
+                       flags,
+                       0,
+                       cancellable,
+                       replace_contents_open_callback,
+                       data);
+}
+  
+/**
+ * g_file_replace_contents_finish:
+ * @file: input #GFile.
+ * @error: a #GAsyncResult. 
+ * @new_etag: a pointer to the new entity tag string for the contents of the file.
+ * @error: a #GError. 
+ * 
+ * Finishes an asynchronous replace of the given @file. 
+ * This function will take ownership of the @new_etag, if present.
+ * 
+ * Returns: %TRUE on success, %FALSE on failure.
+ **/
+gboolean
+g_file_replace_contents_finish (GFile                *file,
+                               GAsyncResult         *res,
+                               char                **new_etag,
+                               GError              **error)
+{
+  GSimpleAsyncResult *simple;
+  ReplaceContentsData *data;
+
+  g_return_val_if_fail (G_IS_FILE (file), FALSE);
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (res), FALSE);
+
+  simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+  
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_replace_contents_async);
+  
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (data->error)
+    {
+      g_propagate_error (error, data->error);
+      data->error = NULL;
+      return FALSE;
+    }
+
+
+  if (new_etag)
+    {
+      *new_etag = data->etag;
+      data->etag = NULL; /* Take ownership */
+    }
+  
+  return TRUE;
+}
diff --git a/gio/gfile.h b/gio/gfile.h
new file mode 100644 (file)
index 0000000..ac3f02f
--- /dev/null
@@ -0,0 +1,676 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_H__
+#define __G_FILE_H__
+
+#include <glib-object.h>
+#include <gio/gfileinfo.h>
+#include <gio/gfileenumerator.h>
+#include <gio/gfileinputstream.h>
+#include <gio/gfileoutputstream.h>
+#include <gio/gmountoperation.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILE            (g_file_get_type ())
+#define G_FILE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_FILE, GFile))
+#define G_IS_FILE(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_FILE))
+#define G_FILE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_FILE, GFileIface))
+
+typedef enum {
+  G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS = (1<<0)
+} GFileQueryInfoFlags;
+
+typedef enum  {
+  G_FILE_CREATE_FLAGS_NONE = 0,
+  G_FILE_CREATE_FLAGS_PRIVATE = (1<<0)
+} GFileCreateFlags;
+
+typedef enum {
+  G_FILE_COPY_OVERWRITE = (1<<0),
+  G_FILE_COPY_BACKUP = (1<<1),
+  G_FILE_COPY_NOFOLLOW_SYMLINKS = (1<<2),
+  G_FILE_COPY_ALL_METADATA = (1<<3)
+} GFileCopyFlags;
+
+typedef enum  {
+  G_FILE_MONITOR_FLAGS_NONE = 0,
+  G_FILE_MONITOR_FLAGS_MONITOR_MOUNTS = (1<<0)
+} GFileMonitorFlags;
+
+typedef struct _GFile                  GFile; /* Dummy typedef */
+typedef struct _GFileIface             GFileIface;
+typedef struct _GDirectoryMonitor       GDirectoryMonitor;
+typedef struct _GFileMonitor            GFileMonitor;
+typedef struct _GVolume         GVolume; /* Dummy typedef */
+
+typedef void (*GFileProgressCallback) (goffset current_num_bytes,
+                                      goffset total_num_bytes,
+                                      gpointer user_data);
+typedef gboolean (* GFileReadMoreCallback) (const char *file_contents,
+                                           goffset file_size,
+                                           gpointer callback_data);
+
+
+struct _GFileIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  GFile *             (*dup)                        (GFile         *file);
+  guint               (*hash)                       (GFile         *file);
+  gboolean            (*equal)                      (GFile         *file1,
+                                                    GFile         *file2);
+  gboolean            (*is_native)                  (GFile         *file);
+  gboolean            (*has_uri_scheme)             (GFile         *file,
+                                                    const char    *uri_scheme);
+  char *              (*get_uri_scheme)             (GFile         *file);
+  char *              (*get_basename)               (GFile         *file);
+  char *              (*get_path)                   (GFile         *file);
+  char *              (*get_uri)                    (GFile         *file);
+  char *              (*get_parse_name)             (GFile         *file);
+  GFile *             (*get_parent)                 (GFile         *file);
+  gboolean            (*contains_file)              (GFile         *parent,
+                                                    GFile         *descendant);
+  char *              (*get_relative_path)          (GFile         *parent,
+                                                    GFile         *descendant);
+  GFile *             (*resolve_relative_path)      (GFile        *file,
+                                                    const char   *relative_path);
+  GFile *             (*get_child_for_display_name) (GFile        *file,
+                                                    const char   *display_name,
+                                                    GError      **error);
+  
+  GFileEnumerator *   (*enumerate_children)        (GFile                *file,
+                                                   const char           *attributes,
+                                                   GFileQueryInfoFlags   flags,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+  void                (*enumerate_children_async)  (GFile                      *file,
+                                                   const char                 *attributes,
+                                                   GFileQueryInfoFlags         flags,
+                                                   int                         io_priority,
+                                                   GCancellable               *cancellable,
+                                                   GAsyncReadyCallback         callback,
+                                                   gpointer                    user_data);
+  GFileEnumerator *   (*enumerate_children_finish) (GFile                      *file,
+                                                   GAsyncResult               *res,
+                                                   GError                    **error);
+  
+  GFileInfo *         (*query_info)         (GFile                *file,
+                                            const char           *attributes,
+                                            GFileQueryInfoFlags   flags,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*query_info_async)   (GFile                *file,
+                                            const char           *attributes,
+                                            GFileQueryInfoFlags   flags,
+                                            int                   io_priority,
+                                            GCancellable         *cancellable,
+                                            GAsyncReadyCallback   callback,
+                                            gpointer              user_data);
+  GFileInfo *         (*query_info_finish)  (GFile                *file,
+                                            GAsyncResult         *res,
+                                            GError              **error);
+  
+  GFileInfo *         (*query_filesystem_info)(GFile                *file,
+                                            const char           *attributes,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*_query_filesystem_info_async) (void);
+  void                (*_query_filesystem_info_finish) (void);
+  
+  GVolume *           (*find_enclosing_volume)(GFile              *file,
+                                              GCancellable       *cancellable,
+                                              GError            **error);
+  void                (*find_enclosing_volume_async)(GFile              *file,
+                                                    int                   io_priority,
+                                                    GCancellable         *cancellable,
+                                                    GAsyncReadyCallback   callback,
+                                                    gpointer              user_data);
+  GVolume *           (*find_enclosing_volume_finish)(GFile              *file,
+                                                     GAsyncResult         *res,
+                                                     GError            **error);
+  
+  GFile *             (*set_display_name)         (GFile                *file,
+                                                  const char           *display_name,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+  void                (*set_display_name_async)   (GFile                      *file,
+                                                  const char                 *display_name,
+                                                  int                         io_priority,
+                                                  GCancellable               *cancellable,
+                                                  GAsyncReadyCallback         callback,
+                                                  gpointer                    user_data);
+  GFile *              (*set_display_name_finish) (GFile                      *file,
+                                                  GAsyncResult               *res,
+                                                  GError                    **error);
+  
+  GFileAttributeInfoList * (*query_settable_attributes) (GFile        *file,
+                                                        GCancellable *cancellable,
+                                                        GError      **error);
+  void                (*_query_settable_attributes_async) (void);
+  void                (*_query_settable_attributes_finish) (void);
+  
+  GFileAttributeInfoList * (*query_writable_namespaces) (GFile        *file,
+                                                        GCancellable *cancellable,
+                                                        GError      **error);
+  void                (*_query_writable_namespaces_async) (void);
+  void                (*_query_writable_namespaces_finish) (void);
+  
+  gboolean            (*set_attribute)            (GFile                *file,
+                                                  const char           *attribute,
+                                                  const GFileAttributeValue *value,
+                                                  GFileQueryInfoFlags   flags,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+  gboolean            (*set_attributes_from_info) (GFile          *file,
+                                                  GFileInfo            *info,
+                                                  GFileQueryInfoFlags   flags,
+                                                  GCancellable         *cancellable,
+                                                  GError              **error);
+  void                (*set_attributes_async)     (GFile                      *file,
+                                                  GFileInfo                  *info,
+                                                  GFileQueryInfoFlags        flags,
+                                                  int                         io_priority,
+                                                  GCancellable               *cancellable,
+                                                  GAsyncReadyCallback         callback,
+                                                  gpointer                    user_data);
+  gboolean            (*set_attributes_finish)    (GFile                      *file,
+                                                  GAsyncResult               *result,
+                                                  GFileInfo                 **info,
+                                                  GError                    **error);
+  
+  GFileInputStream *  (*read)               (GFile                *file,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*read_async)         (GFile                *file,
+                                            int                   io_priority,
+                                            GCancellable         *cancellable,
+                                            GAsyncReadyCallback   callback,
+                                            gpointer              user_data);
+  GFileInputStream *  (*read_finish)        (GFile                *file,
+                                            GAsyncResult         *res,
+                                            GError              **error);
+  
+  GFileOutputStream * (*append_to)          (GFile                *file,
+                                            GFileCreateFlags      flags,
+                                            GCancellable         *cancellable,
+                                            GError               **error);
+  void                 (*append_to_async)   (GFile                      *file,
+                                            GFileCreateFlags            flags,
+                                            int                         io_priority,
+                                            GCancellable               *cancellable,
+                                            GAsyncReadyCallback         callback,
+                                            gpointer                    user_data);
+  GFileOutputStream *  (*append_to_finish)  (GFile                      *file,
+                                            GAsyncResult               *res,
+                                            GError                    **error);
+  
+  GFileOutputStream * (*create)             (GFile                *file,
+                                            GFileCreateFlags      flags,
+                                            GCancellable         *cancellable,
+                                            GError               **error);
+  void                 (*create_async)      (GFile                      *file,
+                                            GFileCreateFlags            flags,
+                                            int                         io_priority,
+                                            GCancellable               *cancellable,
+                                            GAsyncReadyCallback         callback,
+                                            gpointer                    user_data);
+  GFileOutputStream *  (*create_finish)     (GFile                      *file,
+                                            GAsyncResult               *res,
+                                            GError                    **error);
+  
+  GFileOutputStream *  (*replace)           (GFile                *file,
+                                            const char           *etag,
+                                            gboolean              make_backup,
+                                            GFileCreateFlags      flags,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                 (*replace_async)     (GFile                      *file,
+                                            const char                 *etag,
+                                            gboolean                    make_backup,
+                                            GFileCreateFlags            flags,
+                                            int                         io_priority,
+                                            GCancellable               *cancellable,
+                                            GAsyncReadyCallback         callback,
+                                            gpointer                    user_data);
+  GFileOutputStream *  (*replace_finish)    (GFile                      *file,
+                                            GAsyncResult               *res,
+                                            GError                    **error);
+  
+  gboolean            (*delete_file)        (GFile                *file,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*_delete_file_async) (void);
+  void                (*_delete_file_finish) (void);
+  
+  gboolean            (*trash)              (GFile                *file,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*_trash_async) (void);
+  void                (*_trash_finish) (void);
+  
+  gboolean            (*make_directory)     (GFile                *file,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*_make_directory_async) (void);
+  void                (*_make_directory_finish) (void);
+  
+  gboolean            (*make_symbolic_link) (GFile                *file,
+                                            const char           *symlink_value,
+                                            GCancellable         *cancellable,
+                                            GError              **error);
+  void                (*_make_symbolic_link_async) (void);
+  void                (*_make_symbolic_link_finish) (void);
+  
+  gboolean            (*copy)               (GFile                *source,
+                                            GFile                *destination,
+                                            GFileCopyFlags        flags,
+                                            GCancellable         *cancellable,
+                                            GFileProgressCallback progress_callback,
+                                            gpointer              progress_callback_data,
+                                            GError              **error);
+  void                (*_copy_async) (void);
+  void                (*_copy_finish) (void);
+  
+  gboolean            (*move)               (GFile                *source,
+                                            GFile                *destination,
+                                            GFileCopyFlags        flags,
+                                            GCancellable         *cancellable,
+                                            GFileProgressCallback progress_callback,
+                                            gpointer              progress_callback_data,
+                                            GError              **error);
+
+  void                (*_move_async) (void);
+  void                (*_move_finish) (void);
+
+
+  void                (*mount_mountable)           (GFile               *file,
+                                                   GMountOperation     *mount_operation,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback  callback,
+                                                   gpointer             user_data);
+  GFile *             (*mount_mountable_finish)    (GFile               *file,
+                                                   GAsyncResult        *result,
+                                                   GError             **error);
+  void                (*unmount_mountable)         (GFile               *file,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback  callback,
+                                                   gpointer             user_data);
+  gboolean            (*unmount_mountable_finish)  (GFile               *file,
+                                                   GAsyncResult        *result,
+                                                   GError             **error);
+  void                (*eject_mountable)           (GFile               *file,
+                                                   GCancellable        *cancellable,
+                                                   GAsyncReadyCallback  callback,
+                                                   gpointer             user_data);
+  gboolean            (*eject_mountable_finish)    (GFile               *file,
+                                                   GAsyncResult        *result,
+                                                   GError             **error);
+
+
+  void     (*mount_for_location)        (GFile *location,
+                                        GMountOperation *mount_operation,
+                                        GCancellable *cancellable,
+                                        GAsyncReadyCallback callback,
+                                        gpointer user_data);
+  gboolean (*mount_for_location_finish) (GFile *location,
+                                        GAsyncResult *result,
+                                        GError **error);
+  
+  GDirectoryMonitor* (*monitor_dir)         (GFile                  *file,
+                                            GFileMonitorFlags       flags,
+                                            GCancellable           *cancellable);
+
+  GFileMonitor*      (*monitor_file)        (GFile                  *file,
+                                            GFileMonitorFlags       flags,
+                                            GCancellable           *cancellable);
+};
+
+GType g_file_get_type (void) G_GNUC_CONST;
+
+GFile *                 g_file_new_for_path               (const char                 *path);
+GFile *                 g_file_new_for_uri                (const char                 *uri);
+GFile *                 g_file_new_for_commandline_arg    (const char                 *arg);
+GFile *                 g_file_parse_name                 (const char                 *parse_name);
+GFile *                 g_file_dup                        (GFile                      *file);
+guint                   g_file_hash                       (gconstpointer               file);
+gboolean                g_file_equal                      (GFile                      *file1,
+                                                          GFile                      *file2);
+char *                  g_file_get_basename               (GFile                      *file);
+char *                  g_file_get_path                   (GFile                      *file);
+char *                  g_file_get_uri                    (GFile                      *file);
+char *                  g_file_get_parse_name             (GFile                      *file);
+GFile *                 g_file_get_parent                 (GFile                      *file);
+GFile *                 g_file_get_child                  (GFile                      *file,
+                                                          const char                 *name);
+GFile *                 g_file_get_child_for_display_name (GFile                      *file,
+                                                          const char                 *display_name,
+                                                          GError                    **error);
+gboolean                g_file_contains_file              (GFile                      *parent,
+                                                          GFile                      *descendant);
+char *                  g_file_get_relative_path          (GFile                      *parent,
+                                                          GFile                      *descendant);
+GFile *                 g_file_resolve_relative_path      (GFile                      *file,
+                                                          const char                 *relative_path);
+gboolean                g_file_is_native                  (GFile                      *file);
+gboolean                g_file_has_uri_scheme             (GFile                      *file,
+                                                          const char                 *uri_scheme);
+char *                  g_file_get_uri_scheme             (GFile                      *file);
+GFileInputStream *      g_file_read                       (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_file_read_async                 (GFile                      *file,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFileInputStream *      g_file_read_finish                (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+GFileOutputStream *     g_file_append_to                  (GFile                      *file,
+                                                          GFileCreateFlags             flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+GFileOutputStream *     g_file_create                     (GFile                      *file,
+                                                          GFileCreateFlags             flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+GFileOutputStream *     g_file_replace                    (GFile                      *file,
+                                                          const char                 *etag,
+                                                          gboolean                    make_backup,
+                                                          GFileCreateFlags            flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_file_append_to_async            (GFile                      *file,
+                                                          GFileCreateFlags            flags,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFileOutputStream *     g_file_append_to_finish           (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+void                    g_file_create_async               (GFile                      *file,
+                                                          GFileCreateFlags            flags,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFileOutputStream *     g_file_create_finish              (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+void                    g_file_replace_async              (GFile                      *file,
+                                                          const char                 *etag,
+                                                          gboolean                    make_backup,
+                                                          GFileCreateFlags            flags,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFileOutputStream *     g_file_replace_finish             (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+GFileInfo *             g_file_query_info                 (GFile                      *file,
+                                                          const char                 *attributes,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_file_query_info_async           (GFile                      *file,
+                                                          const char                 *attributes,
+                                                          GFileQueryInfoFlags         flags,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFileInfo *             g_file_query_info_finish          (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+GFileInfo *             g_file_query_filesystem_info      (GFile                      *file,
+                                                          const char                 *attributes,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+GVolume *               g_file_find_enclosing_volume      (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+GFileEnumerator *       g_file_enumerate_children         (GFile                      *file,
+                                                          const char                 *attributes,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_file_enumerate_children_async   (GFile                      *file,
+                                                          const char                 *attributes,
+                                                          GFileQueryInfoFlags         flags,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFileEnumerator *       g_file_enumerate_children_finish  (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+GFile *                 g_file_set_display_name           (GFile                      *file,
+                                                          const char                 *display_name,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_file_set_display_name_async     (GFile                      *file,
+                                                          const char                 *display_name,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFile *                 g_file_set_display_name_finish    (GFile                      *file,
+                                                          GAsyncResult               *res,
+                                                          GError                    **error);
+gboolean                g_file_delete                     (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_trash                      (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_copy                       (GFile                      *source,
+                                                          GFile                      *destination,
+                                                          GFileCopyFlags              flags,
+                                                          GCancellable               *cancellable,
+                                                          GFileProgressCallback       progress_callback,
+                                                          gpointer                    progress_callback_data,
+                                                          GError                    **error);
+gboolean                g_file_move                       (GFile                      *source,
+                                                          GFile                      *destination,
+                                                          GFileCopyFlags              flags,
+                                                          GCancellable               *cancellable,
+                                                          GFileProgressCallback       progress_callback,
+                                                          gpointer                    progress_callback_data,
+                                                          GError                    **error);
+gboolean                g_file_make_directory             (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_make_symbolic_link         (GFile                      *file,
+                                                          const char                 *symlink_value,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+GFileAttributeInfoList *g_file_query_settable_attributes  (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+GFileAttributeInfoList *g_file_query_writable_namespaces  (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute              (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          const GFileAttributeValue  *value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attributes_from_info   (GFile                      *file,
+                                                          GFileInfo                  *info,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_file_set_attributes_async       (GFile                      *file,
+                                                          GFileInfo                  *info,
+                                                          GFileQueryInfoFlags         flags,
+                                                          int                         io_priority,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+gboolean                g_file_set_attributes_finish      (GFile                      *file,
+                                                          GAsyncResult               *result,
+                                                          GFileInfo                 **info,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute_string       (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          const char                 *value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute_byte_string  (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          const char                 *value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute_uint32       (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          guint32                     value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute_int32        (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          gint32                      value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute_uint64       (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          guint64                     value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+gboolean                g_file_set_attribute_int64        (GFile                      *file,
+                                                          const char                 *attribute,
+                                                          gint64                      value,
+                                                          GFileQueryInfoFlags         flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+void                    g_mount_for_location              (GFile                      *location,
+                                                          GMountOperation            *mount_operation,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+gboolean                g_mount_for_location_finish       (GFile                      *location,
+                                                          GAsyncResult               *result,
+                                                          GError                    **error);
+void                    g_file_mount_mountable            (GFile                      *file,
+                                                          GMountOperation            *mount_operation,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+GFile *                 g_file_mount_mountable_finish     (GFile                      *file,
+                                                          GAsyncResult               *result,
+                                                          GError                    **error);
+void                    g_file_unmount_mountable          (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+gboolean                g_file_unmount_mountable_finish   (GFile                      *file,
+                                                          GAsyncResult               *result,
+                                                          GError                    **error);
+void                    g_file_eject_mountable            (GFile                      *file,
+                                                          GCancellable               *cancellable,
+                                                          GAsyncReadyCallback         callback,
+                                                          gpointer                    user_data);
+gboolean                g_file_eject_mountable_finish     (GFile                      *file,
+                                                          GAsyncResult               *result,
+                                                          GError                    **error);
+
+gboolean                g_file_copy_attributes            (GFile                      *source,
+                                                          GFile                      *destination,
+                                                          GFileCopyFlags              flags,
+                                                          GCancellable               *cancellable,
+                                                          GError                    **error);
+
+
+GDirectoryMonitor* g_file_monitor_directory          (GFile                  *file,
+                                                     GFileMonitorFlags       flags,
+                                                     GCancellable           *cancellable);
+GFileMonitor*      g_file_monitor_file               (GFile                  *file,
+                                                     GFileMonitorFlags       flags,
+                                                     GCancellable           *cancellable);
+
+
+/* Utilities */
+
+gboolean g_file_load_contents                (GFile                  *file,
+                                             GCancellable           *cancellable,
+                                             char                  **contents,
+                                             gsize                  *length,
+                                             char                  **etag_out,
+                                             GError                **error);
+void     g_file_load_contents_async          (GFile                  *file,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+gboolean g_file_load_contents_finish         (GFile                  *file,
+                                             GAsyncResult           *res,
+                                             char                  **contents,
+                                             gsize                  *length,
+                                             char                  **etag_out,
+                                             GError                **error);
+void     g_file_load_partial_contents_async  (GFile                  *file,
+                                             GCancellable           *cancellable,
+                                             GFileReadMoreCallback   read_more_callback,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+gboolean g_file_load_partial_contents_finish (GFile                  *file,
+                                             GAsyncResult           *res,
+                                             char                  **contents,
+                                             gsize                  *length,
+                                             char                  **etag_out,
+                                             GError                **error);
+gboolean g_file_replace_contents             (GFile                  *file,
+                                             const char             *contents,
+                                             gsize                   length,
+                                             const char             *etag,
+                                             gboolean                make_backup,
+                                             GFileCreateFlags        flags,
+                                             char                  **new_etag,
+                                             GCancellable           *cancellable,
+                                             GError                **error);
+void     g_file_replace_contents_async       (GFile                  *file,
+                                             const char             *contents,
+                                             gsize                   length,
+                                             const char             *etag,
+                                             gboolean                make_backup,
+                                             GFileCreateFlags        flags,
+                                             GCancellable           *cancellable,
+                                             GAsyncReadyCallback     callback,
+                                             gpointer                user_data);
+gboolean g_file_replace_contents_finish      (GFile                  *file,
+                                             GAsyncResult           *res,
+                                             char                  **new_etag,
+                                             GError                **error);
+
+G_END_DECLS
+
+#endif /* __G_FILE_H__ */
diff --git a/gio/gfileattribute.c b/gio/gfileattribute.c
new file mode 100644 (file)
index 0000000..512500b
--- /dev/null
@@ -0,0 +1,704 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "gfileattribute.h"
+#include <glib-object.h>
+#include "glibintl.h"
+
+/**
+ * g_file_attribute_value_free:
+ * @attr: a #GFileAttributeValue. 
+ * 
+ * Frees the memory used by @attr.
+ *
+ **/
+void
+g_file_attribute_value_free (GFileAttributeValue *attr)
+{
+  g_return_if_fail (attr != NULL);
+
+  g_file_attribute_value_clear (attr);
+  g_free (attr);
+}
+
+/**
+ * g_file_attribute_value_clear:
+ * @attr: a #GFileAttributeValue.
+ *
+ * Clears the value of @attr and sets its type to 
+ * %G_FILE_ATTRIBUTE_TYPE_INVALID.
+ * 
+ **/
+void
+g_file_attribute_value_clear (GFileAttributeValue *attr)
+{
+  g_return_if_fail (attr != NULL);
+
+  if (attr->type == G_FILE_ATTRIBUTE_TYPE_STRING ||
+      attr->type == G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
+    g_free (attr->u.string);
+  
+  if (attr->type == G_FILE_ATTRIBUTE_TYPE_OBJECT &&
+      attr->u.obj != NULL)
+    g_object_unref (attr->u.obj);
+  
+  attr->type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+}
+
+/**
+ * g_file_attribute_value_set:
+ * @attr: a #GFileAttributeValue.
+ * @new_value:
+ * 
+ **/
+void
+g_file_attribute_value_set (GFileAttributeValue *attr,
+                           const GFileAttributeValue *new_value)
+{
+  g_return_if_fail (attr != NULL);
+  g_return_if_fail (new_value != NULL);
+
+  g_file_attribute_value_clear (attr);
+  *attr = *new_value;
+
+  if (attr->type == G_FILE_ATTRIBUTE_TYPE_STRING ||
+      attr->type == G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
+    attr->u.string = g_strdup (attr->u.string);
+  
+  if (attr->type == G_FILE_ATTRIBUTE_TYPE_OBJECT &&
+      attr->u.obj != NULL)
+    g_object_ref (attr->u.obj);
+}
+
+/**
+ * g_file_attribute_value_new:
+ * 
+ * Returns: a new #GFileAttributeValue.
+ **/
+GFileAttributeValue *
+g_file_attribute_value_new (void)
+{
+  GFileAttributeValue *attr;
+
+  attr = g_new (GFileAttributeValue, 1);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+  return attr;
+}
+
+
+/**
+ * g_file_attribute_value_dup:
+ * @other: a #GFileAttributeValue to duplicate.
+ * 
+ * Returns: a duplicate of the @other.
+ **/
+GFileAttributeValue *
+g_file_attribute_value_dup (const GFileAttributeValue *other)
+{
+  GFileAttributeValue *attr;
+
+  g_return_val_if_fail (other != NULL, NULL);
+
+  attr = g_new (GFileAttributeValue, 1);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+  g_file_attribute_value_set (attr, other);
+  return attr;
+}
+
+static gboolean
+valid_char (char c)
+{
+  return c >= 32 && c <= 126 && c != '\\';
+}
+
+static char *
+escape_byte_string (const char *str)
+{
+  size_t len;
+  int num_invalid, i;
+  char *escaped_val, *p;
+  unsigned char c;
+  char *hex_digits = "0123456789abcdef";
+  
+  len = strlen (str);
+  
+  num_invalid = 0;
+  for (i = 0; i < len; i++)
+    {
+      if (!valid_char (str[i]))
+       num_invalid++;
+    }
+       
+  if (num_invalid == 0)
+    return g_strdup (str);
+  else
+    {
+      escaped_val = g_malloc (len + num_invalid*3 + 1);
+
+      p = escaped_val;
+      for (i = 0; i < len; i++)
+       {
+         c = str[i];
+         if (valid_char (c))
+           *p++ = c;
+         else
+           {
+             *p++ = '\\';
+             *p++ = 'x';
+             *p++ = hex_digits[(c >> 8) & 0xf];
+             *p++ = hex_digits[c & 0xf];
+           }
+       }
+      *p++ = 0;
+      return escaped_val;
+    }
+}
+
+/**
+ * g_file_attribute_value_as_string:
+ * @attr: a #GFileAttributeValue.
+ *
+ * Converts a #GFileAttributeValue to a string for display.
+ * The returned string should be freed when no longer needed
+ * 
+ * Returns: a string from the @attr, %NULL on error, or "<invalid>" if 
+ * @attr is of type %G_FILE_ATTRIBUTE_TYPE_INVALID.
+ **/
+char *
+g_file_attribute_value_as_string (const GFileAttributeValue *attr)
+{
+  char *str;
+
+  g_return_val_if_fail (attr != NULL, NULL);
+
+  switch (attr->type)
+    {
+    case G_FILE_ATTRIBUTE_TYPE_STRING:
+      str = g_strdup (attr->u.string);
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_BYTE_STRING:
+      str = escape_byte_string (attr->u.string);
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_BOOLEAN:
+      str = g_strdup_printf ("%s", attr->u.boolean?"TRUE":"FALSE");
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_UINT32:
+      str = g_strdup_printf ("%u", (unsigned int)attr->u.uint32);
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_INT32:
+      str = g_strdup_printf ("%i", (int)attr->u.int32);
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_UINT64:
+      str = g_strdup_printf ("%"G_GUINT64_FORMAT, attr->u.uint64);
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_INT64:
+      str = g_strdup_printf ("%"G_GINT64_FORMAT, attr->u.int64);
+      break;
+    case G_FILE_ATTRIBUTE_TYPE_OBJECT:
+      str = g_strdup_printf ("%s:%p", g_type_name_from_instance
+                                          ((GTypeInstance *) attr->u.obj),
+                                      attr->u.obj);
+      break;
+    default:
+      g_warning ("Invalid type in GFileInfo attribute");
+      str = g_strdup ("<invalid>");
+      break;
+    }
+  
+  return str;
+}
+
+/**
+ * g_file_attribute_value_get_string:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_attribute_value_get_string (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return NULL;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_STRING, NULL);
+
+  return attr->u.string;
+}
+
+/**
+ * g_file_attribute_value_get_byte_string:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_attribute_value_get_byte_string (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return NULL;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_BYTE_STRING, NULL);
+
+  return attr->u.string;
+}
+  
+/**
+ * g_file_attribute_value_get_boolean:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+gboolean
+g_file_attribute_value_get_boolean (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return FALSE;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_BOOLEAN, FALSE);
+
+  return attr->u.boolean;
+}
+  
+/**
+ * g_file_attribute_value_get_uint32:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+guint32
+g_file_attribute_value_get_uint32 (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return 0;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_UINT32, 0);
+
+  return attr->u.uint32;
+}
+
+/**
+ * g_file_attribute_value_get_int32:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+gint32
+g_file_attribute_value_get_int32 (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return 0;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_INT32, 0);
+
+  return attr->u.int32;
+}
+
+/**
+ * g_file_attribute_value_get_uint64:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/  
+guint64
+g_file_attribute_value_get_uint64 (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return 0;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_UINT64, 0);
+
+  return attr->u.uint64;
+}
+
+/**
+ * g_file_attribute_value_get_int64:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+gint64
+g_file_attribute_value_get_int64 (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return 0;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_INT64, 0);
+
+  return attr->u.int64;
+}
+
+/**
+ * g_file_attribute_value_get_object:
+ * @attr: a #GFileAttributeValue.
+ * 
+ * Returns: 
+ **/
+GObject *
+g_file_attribute_value_get_object (const GFileAttributeValue *attr)
+{
+  if (attr == NULL)
+    return NULL;
+
+  g_return_val_if_fail (attr->type == G_FILE_ATTRIBUTE_TYPE_OBJECT, NULL);
+
+  return attr->u.obj;
+}
+  
+/**
+ * g_file_attribute_value_set_string:
+ * @attr: a #GFileAttributeValue.
+ * @string:
+ * 
+ **/
+void
+g_file_attribute_value_set_string (GFileAttributeValue *attr,
+                                  const char          *string)
+{
+  g_return_if_fail (attr != NULL);
+  g_return_if_fail (string != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_STRING;
+  attr->u.string = g_strdup (string);
+}
+
+/**
+ * g_file_attribute_value_set_byte_string:
+ * @attr: a #GFileAttributeValue.
+ * @string:
+ * 
+ **/
+void
+g_file_attribute_value_set_byte_string (GFileAttributeValue *attr,
+                                       const char          *string)
+{
+  g_return_if_fail (attr != NULL);
+  g_return_if_fail (string != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_BYTE_STRING;
+  attr->u.string = g_strdup (string);
+}
+
+/**
+ * g_file_attribute_value_set_boolean:
+ * @attr: a #GFileAttributeValue.
+ * @value:
+ *  
+ **/
+void
+g_file_attribute_value_set_boolean (GFileAttributeValue *attr,
+                                   gboolean             value)
+{
+  g_return_if_fail (attr != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_BOOLEAN;
+  attr->u.boolean = !!value;
+}
+
+/**
+ * g_file_attribute_value_set_uint32:
+ * @attr: a #GFileAttributeValue.
+ * @value:
+ * 
+ **/ 
+void
+g_file_attribute_value_set_uint32 (GFileAttributeValue *attr,
+                                  guint32              value)
+{
+  g_return_if_fail (attr != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_UINT32;
+  attr->u.uint32 = value;
+}
+
+/**
+ * g_file_attribute_value_set_int32:
+ * @attr: a #GFileAttributeValue.
+ * @value:
+ *  
+ **/
+void
+g_file_attribute_value_set_int32 (GFileAttributeValue *attr,
+                                 gint32               value)
+{
+  g_return_if_fail (attr != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_INT32;
+  attr->u.int32 = value;
+}
+
+/**
+ * g_file_attribute_value_set_uint64:
+ * @attr: a #GFileAttributeValue.
+ * @value:
+ * 
+ **/
+void
+g_file_attribute_value_set_uint64 (GFileAttributeValue *attr,
+                                  guint64              value)
+{
+  g_return_if_fail (attr != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_UINT64;
+  attr->u.uint64 = value;
+}
+
+/**
+ * g_file_attribute_value_set_int64:
+ * @attr: a #GFileAttributeValue.
+ * @value: a #gint64 to set the value to.
+ *  
+ **/
+void
+g_file_attribute_value_set_int64 (GFileAttributeValue *attr,
+                                 gint64               value)
+{
+  g_return_if_fail (attr != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_INT64;
+  attr->u.int64 = value;
+}
+
+/**
+ * g_file_attribute_value_set_object:
+ * @attr: a #GFileAttributeValue.
+ * @obj: a #GObject.
+ *
+ * Sets the file attribute @attr to contain the value @obj.
+ * The @attr references the object internally.
+ * 
+ **/
+void
+g_file_attribute_value_set_object (GFileAttributeValue *attr,
+                                  GObject             *obj)
+{
+  g_return_if_fail (attr != NULL);
+  g_return_if_fail (obj != NULL);
+
+  g_file_attribute_value_clear (attr);
+  attr->type = G_FILE_ATTRIBUTE_TYPE_OBJECT;
+  attr->u.obj = g_object_ref (obj);
+}
+
+typedef struct {
+  GFileAttributeInfoList public;
+  GArray *array;
+  int ref_count;
+} GFileAttributeInfoListPriv;
+
+static void
+list_update_public (GFileAttributeInfoListPriv *priv)
+{
+  priv->public.infos = (GFileAttributeInfo *)priv->array->data;
+  priv->public.n_infos = priv->array->len;
+}
+
+/**
+ * g_file_attribute_info_list_new:
+ * 
+ * Returns a new #GFileAttributeInfoList.
+ **/
+GFileAttributeInfoList *
+g_file_attribute_info_list_new (void)
+{
+  GFileAttributeInfoListPriv *priv;
+
+  priv = g_new0 (GFileAttributeInfoListPriv, 1);
+  
+  priv->ref_count = 1;
+  priv->array = g_array_new (TRUE, FALSE, sizeof (GFileAttributeInfo));
+  
+  list_update_public (priv);
+  
+  return (GFileAttributeInfoList *)priv;
+}
+
+/**
+ * g_file_attribute_info_list_dup:
+ * @list: a #GFileAttributeInfoList to duplicate.
+ * 
+ * Returns a duplicate of the given @list. 
+ **/
+GFileAttributeInfoList *
+g_file_attribute_info_list_dup (GFileAttributeInfoList *list)
+{
+  GFileAttributeInfoListPriv *new;
+  int i;
+  
+  g_return_val_if_fail (list != NULL, NULL);
+
+  new = g_new0 (GFileAttributeInfoListPriv, 1);
+  new->ref_count = 1;
+  new->array = g_array_new (TRUE, FALSE, sizeof (GFileAttributeInfo));
+
+  g_array_set_size (new->array, list->n_infos);
+  list_update_public (new);
+  for (i = 0; i < list->n_infos; i++)
+    {
+      new->public.infos[i].name = g_strdup (list->infos[i].name);
+      new->public.infos[i].type = list->infos[i].type;
+      new->public.infos[i].flags = list->infos[i].flags;
+    }
+  
+  return (GFileAttributeInfoList *)new;
+}
+
+/**
+ * g_file_attribute_info_list_ref:
+ * @list: a #GFileAttributeInfoList to reference.
+ * 
+ * Returns: #GFileAttributeInfoList or %NULL on error.
+ **/
+GFileAttributeInfoList *
+g_file_attribute_info_list_ref (GFileAttributeInfoList *list)
+{
+  GFileAttributeInfoListPriv *priv = (GFileAttributeInfoListPriv *)list;
+  
+  g_return_val_if_fail (list != NULL, NULL);
+  g_return_val_if_fail (priv->ref_count > 0, NULL);
+  
+  g_atomic_int_inc (&priv->ref_count);
+  
+  return list;
+}
+
+/**
+ * g_file_attribute_info_list_unref:
+ * @list: The #GFileAttributeInfoList to unreference.
+ * 
+ * Removes a reference from the given @list. If the reference count
+ * falls to zero, the @list is deleted.
+ **/
+void
+g_file_attribute_info_list_unref (GFileAttributeInfoList *list)
+{
+  GFileAttributeInfoListPriv *priv = (GFileAttributeInfoListPriv *)list;
+  int i;
+  
+  g_return_if_fail (list != NULL);
+  g_return_if_fail (priv->ref_count > 0);
+  
+  if (g_atomic_int_dec_and_test (&priv->ref_count))
+    {
+      for (i = 0; i < list->n_infos; i++)
+        g_free (list->infos[i].name);
+      g_array_free (priv->array, TRUE);
+    }
+}
+
+static int
+g_file_attribute_info_list_bsearch (GFileAttributeInfoList *list,
+                                   const char             *name)
+{
+  int start, end, mid;
+  
+  start = 0;
+  end = list->n_infos;
+
+  while (start != end)
+    {
+      mid = start + (end - start) / 2;
+
+      if (strcmp (name, list->infos[mid].name) < 0)
+       end = mid;
+      else if (strcmp (name, list->infos[mid].name) > 0)
+       start = mid + 1;
+      else
+       return mid;
+    }
+  return start;
+}
+
+/**
+ * g_file_attribute_info_list_lookup:
+ * @list: a #GFileAttributeInfoList.
+ * @name: the name of the attribute to lookup.
+ * 
+ * Returns: a #GFileAttributeInfo for the @name, or %NULL if an 
+ * attribute isn't found.
+ **/
+const GFileAttributeInfo *
+g_file_attribute_info_list_lookup (GFileAttributeInfoList *list,
+                                  const char             *name)
+{
+  int i;
+  
+  g_return_val_if_fail (list != NULL, NULL);
+  g_return_val_if_fail (name != NULL, NULL);
+  
+  i = g_file_attribute_info_list_bsearch (list, name);
+
+  if (i < list->n_infos && strcmp (list->infos[i].name, name) == 0)
+    return &list->infos[i];
+  
+  return NULL;
+}
+
+/**
+ * g_file_attribute_info_list_add:
+ * @list: a #GFileAttributeInfoList.
+ * @name: the name of the attribute to add.
+ * @type: the #GFileAttributeType for the attribute.
+ * @flags: #GFileAttributeFlags for the attribute.
+ * 
+ * Adds a new attribute with @name to the @list, setting
+ * its @type and @flags. 
+ * 
+ **/
+void
+g_file_attribute_info_list_add    (GFileAttributeInfoList *list,
+                                  const char             *name,
+                                  GFileAttributeType      type,
+                                  GFileAttributeFlags     flags)
+{
+  GFileAttributeInfoListPriv *priv = (GFileAttributeInfoListPriv *)list;
+  GFileAttributeInfo info;
+  int i;
+  
+  g_return_if_fail (list != NULL);
+  g_return_if_fail (name != NULL);
+
+  i = g_file_attribute_info_list_bsearch (list, name);
+
+  if (i < list->n_infos && strcmp (list->infos[i].name, name) == 0)
+    {
+      list->infos[i].type = type;
+      return;
+    }
+
+  info.name = g_strdup (name);
+  info.type = type;
+  info.flags = flags;
+  g_array_insert_vals (priv->array, i, &info, 1);
+
+  list_update_public (priv);
+}
diff --git a/gio/gfileattribute.h b/gio/gfileattribute.h
new file mode 100644 (file)
index 0000000..125a46d
--- /dev/null
@@ -0,0 +1,132 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_ATTRIBUTE_H__
+#define __G_FILE_ATTRIBUTE_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+  G_FILE_ATTRIBUTE_TYPE_INVALID = 0,
+  G_FILE_ATTRIBUTE_TYPE_STRING,
+  G_FILE_ATTRIBUTE_TYPE_BYTE_STRING, /* zero terminated string of non-zero bytes */
+  G_FILE_ATTRIBUTE_TYPE_BOOLEAN,
+  G_FILE_ATTRIBUTE_TYPE_UINT32,
+  G_FILE_ATTRIBUTE_TYPE_INT32,
+  G_FILE_ATTRIBUTE_TYPE_UINT64,
+  G_FILE_ATTRIBUTE_TYPE_INT64,
+  G_FILE_ATTRIBUTE_TYPE_OBJECT
+} GFileAttributeType;
+
+typedef enum {
+  G_FILE_ATTRIBUTE_FLAGS_NONE = 0,
+  G_FILE_ATTRIBUTE_FLAGS_COPY_WITH_FILE = 1 << 0,
+  G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED = 1 << 1
+} GFileAttributeFlags;
+
+/* Used by g_file_set_attributes_from_info */
+typedef enum {
+  G_FILE_ATTRIBUTE_STATUS_UNSET = 0,
+  G_FILE_ATTRIBUTE_STATUS_SET,
+  G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING
+} GFileAttributeStatus;
+
+#define G_FILE_ATTRIBUTE_VALUE_INIT {0}
+
+typedef struct  {
+  GFileAttributeType type : 8;
+  GFileAttributeStatus status : 8;
+  union {
+    gboolean boolean;
+    gint32 int32;
+    guint32 uint32;
+    gint64 int64;
+    guint64 uint64;
+    char *string;
+    GQuark quark;
+    GObject *obj;
+  } u;
+} GFileAttributeValue;
+
+typedef struct {
+  char *name;
+  GFileAttributeType type;
+  GFileAttributeFlags flags;
+} GFileAttributeInfo;
+
+typedef struct {
+  GFileAttributeInfo *infos;
+  int n_infos;
+} GFileAttributeInfoList;
+
+GFileAttributeValue *g_file_attribute_value_new             (void);
+void                 g_file_attribute_value_free            (GFileAttributeValue *attr);
+void                 g_file_attribute_value_clear           (GFileAttributeValue *attr);
+void                 g_file_attribute_value_set             (GFileAttributeValue *attr,
+                                                            const GFileAttributeValue *new_value);
+GFileAttributeValue *g_file_attribute_value_dup             (const GFileAttributeValue *other);
+
+char *               g_file_attribute_value_as_string       (const GFileAttributeValue *attr);
+
+const char *         g_file_attribute_value_get_string      (const GFileAttributeValue *attr);
+const char *         g_file_attribute_value_get_byte_string (const GFileAttributeValue *attr);
+gboolean             g_file_attribute_value_get_boolean     (const GFileAttributeValue *attr);
+guint32              g_file_attribute_value_get_uint32      (const GFileAttributeValue *attr);
+gint32               g_file_attribute_value_get_int32       (const GFileAttributeValue *attr);
+guint64              g_file_attribute_value_get_uint64      (const GFileAttributeValue *attr);
+gint64               g_file_attribute_value_get_int64       (const GFileAttributeValue *attr);
+GObject *            g_file_attribute_value_get_object      (const GFileAttributeValue *attr);
+
+void                 g_file_attribute_value_set_string      (GFileAttributeValue *attr,
+                                                            const char          *string);
+void                 g_file_attribute_value_set_byte_string (GFileAttributeValue *attr,
+                                                            const char          *string);
+void                 g_file_attribute_value_set_boolean     (GFileAttributeValue *attr,
+                                                            gboolean             value);
+void                 g_file_attribute_value_set_uint32      (GFileAttributeValue *attr,
+                                                            guint32              value);
+void                 g_file_attribute_value_set_int32       (GFileAttributeValue *attr,
+                                                            gint32               value);
+void                 g_file_attribute_value_set_uint64      (GFileAttributeValue *attr,
+                                                            guint64              value);
+void                 g_file_attribute_value_set_int64       (GFileAttributeValue *attr,
+                                                            gint64               value);
+void                 g_file_attribute_value_set_object      (GFileAttributeValue *attr,
+                                                            GObject             *obj);
+
+GFileAttributeInfoList *  g_file_attribute_info_list_new    (void);
+GFileAttributeInfoList *  g_file_attribute_info_list_ref    (GFileAttributeInfoList *list);
+void                      g_file_attribute_info_list_unref  (GFileAttributeInfoList *list);
+GFileAttributeInfoList *  g_file_attribute_info_list_dup    (GFileAttributeInfoList *list);
+const GFileAttributeInfo *g_file_attribute_info_list_lookup (GFileAttributeInfoList *list,
+                                                            const char             *name);
+void                      g_file_attribute_info_list_add    (GFileAttributeInfoList *list,
+                                                            const char             *name,
+                                                            GFileAttributeType      type,
+                                                            GFileAttributeFlags     flags);
+
+G_END_DECLS
+
+
+#endif /* __G_FILE_INFO_H__ */
diff --git a/gio/gfileenumerator.c b/gio/gfileenumerator.c
new file mode 100644 (file)
index 0000000..300fff3
--- /dev/null
@@ -0,0 +1,617 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gfileenumerator.h"
+#include "gioscheduler.h"
+#include "gasynchelper.h"
+#include "gsimpleasyncresult.h"
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GFileEnumerator, g_file_enumerator, G_TYPE_OBJECT);
+
+struct _GFileEnumeratorPrivate {
+  /* TODO: Should be public for subclasses? */
+  guint closed : 1;
+  guint pending : 1;
+  GAsyncReadyCallback outstanding_callback;
+  GError *outstanding_error;
+};
+
+static void     g_file_enumerator_real_next_files_async  (GFileEnumerator      *enumerator,
+                                                         int                   num_files,
+                                                         int                   io_priority,
+                                                         GCancellable         *cancellable,
+                                                         GAsyncReadyCallback   callback,
+                                                         gpointer              user_data);
+static GList *  g_file_enumerator_real_next_files_finish (GFileEnumerator      *enumerator,
+                                                         GAsyncResult         *res,
+                                                         GError              **error);
+static void     g_file_enumerator_real_close_async       (GFileEnumerator      *enumerator,
+                                                         int                   io_priority,
+                                                         GCancellable         *cancellable,
+                                                         GAsyncReadyCallback   callback,
+                                                         gpointer              user_data);
+static gboolean g_file_enumerator_real_close_finish      (GFileEnumerator      *enumerator,
+                                                         GAsyncResult         *res,
+                                                         GError              **error);
+
+static void
+g_file_enumerator_finalize (GObject *object)
+{
+  GFileEnumerator *enumerator;
+
+  enumerator = G_FILE_ENUMERATOR (object);
+  
+  if (!enumerator->priv->closed)
+    g_file_enumerator_close (enumerator, NULL, NULL);
+
+  if (G_OBJECT_CLASS (g_file_enumerator_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_file_enumerator_parent_class)->finalize) (object);
+}
+
+static void
+g_file_enumerator_class_init (GFileEnumeratorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GFileEnumeratorPrivate));
+  
+  gobject_class->finalize = g_file_enumerator_finalize;
+
+  klass->next_files_async = g_file_enumerator_real_next_files_async;
+  klass->next_files_finish = g_file_enumerator_real_next_files_finish;
+  klass->close_async = g_file_enumerator_real_close_async;
+  klass->close_finish = g_file_enumerator_real_close_finish;
+}
+
+static void
+g_file_enumerator_init (GFileEnumerator *enumerator)
+{
+  enumerator->priv = G_TYPE_INSTANCE_GET_PRIVATE (enumerator,
+                                                 G_TYPE_FILE_ENUMERATOR,
+                                                 GFileEnumeratorPrivate);
+}
+
+/**
+ * g_file_enumerator_next_file:
+ * @enumerator: a #GFileEnumerator.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Returns information for the next file in the enumerated object.
+ * Will block until the information is available.
+ *
+ * On error, returns %NULL and sets @error to the error. If the
+ * enumerator is at the end, %NULL will be returned and @error will
+ * be unset.
+ *
+ * Return value: A #GFileInfo or %NULL on error or end of enumerator
+ **/
+GFileInfo *
+g_file_enumerator_next_file (GFileEnumerator *enumerator,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+  GFileEnumeratorClass *class;
+  GFileInfo *info;
+  
+  g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
+  g_return_val_if_fail (enumerator != NULL, NULL);
+  
+  if (enumerator->priv->closed)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Enumerator is closed"));
+      return NULL;
+    }
+
+  if (enumerator->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("File enumerator has outstanding operation"));
+      return NULL;
+    }
+
+  if (enumerator->priv->outstanding_error)
+    {
+      g_propagate_error (error, enumerator->priv->outstanding_error);
+      enumerator->priv->outstanding_error = NULL;
+      return NULL;
+    }
+  
+  class = G_FILE_ENUMERATOR_GET_CLASS (enumerator);
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  enumerator->priv->pending = TRUE;
+  info = (* class->next_file) (enumerator, cancellable, error);
+  enumerator->priv->pending = FALSE;
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  return info;
+}
+  
+/**
+ * g_file_enumerator_close:
+ * @enumerator: a #GFileEnumerator.
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Releases all resources used by this enumerator, making the
+ * enumerator return %G_IO_ERROR_CLOSED on all calls.
+ *
+ * This will be automatically called when the last reference
+ * is dropped, but you might want to call make sure resources
+ * are released as early as possible.
+ *
+ * Return value: #TRUE on success or #FALSE on error.
+ **/
+gboolean
+g_file_enumerator_close (GFileEnumerator *enumerator,
+                        GCancellable *cancellable,
+                        GError **error)
+{
+  GFileEnumeratorClass *class;
+
+  g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), FALSE);
+  g_return_val_if_fail (enumerator != NULL, FALSE);
+  
+  class = G_FILE_ENUMERATOR_GET_CLASS (enumerator);
+
+  if (enumerator->priv->closed)
+    return TRUE;
+  
+  if (enumerator->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("File enumerator has outstanding operation"));
+      return FALSE;
+    }
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  enumerator->priv->pending = TRUE;
+  (* class->close) (enumerator, cancellable, error);
+  enumerator->priv->pending = FALSE;
+  enumerator->priv->closed = TRUE;
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  return TRUE;
+}
+
+static void
+next_async_callback_wrapper (GObject *source_object,
+                            GAsyncResult *res,
+                            gpointer user_data)
+{
+  GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object);
+
+  enumerator->priv->pending = FALSE;
+  if (enumerator->priv->outstanding_callback)
+    (*enumerator->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (enumerator);
+}
+
+/**
+ * g_file_enumerator_next_files_async:
+ * @enumerator: a #GFileEnumerator.
+ * @num_files: the number of file info objects to request
+ * @io_priority: the io priority of the request. the io priority of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the user_data to pass to callback function 
+ *
+ * Request information for a number of files from the enumerator asynchronously.
+ * When all i/o for the operation is finished the @callback will be called with
+ * the requested information.
+ *
+ * The callback can be called with less than @num_files files in case of error
+ * or at the end of the enumerator. In case of a partial error the callback will
+ * be called with any succeeding items and no error, and on the next request the
+ * error will be reported. If a request is cancelled the callback will be called
+ * with %G_IO_ERROR_CANCELLED.
+ *
+ * During an async request no other sync and async calls are allowed, and will
+ * result in %G_IO_ERROR_PENDING errors. 
+ *
+ * Any outstanding i/o request with higher priority (lower numerical value) will
+ * be executed before an outstanding request with lower priority. Default
+ * priority is %G_PRIORITY_DEFAULT.
+ **/
+void
+g_file_enumerator_next_files_async  (GFileEnumerator                *enumerator,
+                                    int                             num_files,
+                                    int                             io_priority,
+                                    GCancellable                   *cancellable,
+                                    GAsyncReadyCallback             callback,
+                                    gpointer                        user_data)
+{
+  GFileEnumeratorClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator));
+  g_return_if_fail (enumerator != NULL);
+  g_return_if_fail (num_files >= 0);
+
+  if (num_files == 0)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (enumerator),
+                                         callback,
+                                         user_data,
+                                         g_file_enumerator_next_files_async);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+  
+  if (enumerator->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (enumerator),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("File enumerator is already closed"));
+      return;
+    }
+  
+  if (enumerator->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (enumerator),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("File enumerator has outstanding operation"));
+      return;
+    }
+
+  class = G_FILE_ENUMERATOR_GET_CLASS (enumerator);
+  
+  enumerator->priv->pending = TRUE;
+  enumerator->priv->outstanding_callback = callback;
+  g_object_ref (enumerator);
+  (* class->next_files_async) (enumerator, num_files, io_priority, cancellable, 
+                              next_async_callback_wrapper, user_data);
+}
+
+/**
+ * g_file_enumerator_next_files_finish:
+ * @enumerator: a #GFileEnumerator.
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+GList *
+g_file_enumerator_next_files_finish  (GFileEnumerator *enumerator,
+                                     GAsyncResult *result,
+                                     GError **error)
+{
+  GFileEnumeratorClass *class;
+  GSimpleAsyncResult *simple;
+  
+  g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+      
+      /* Special case read of 0 files */
+      if (g_simple_async_result_get_source_tag (simple) == g_file_enumerator_next_files_async)
+       return NULL;
+    }
+  
+  class = G_FILE_ENUMERATOR_GET_CLASS (enumerator);
+  return class->next_files_finish (enumerator, result, error);
+}
+
+static void
+close_async_callback_wrapper (GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer user_data)
+{
+  GFileEnumerator *enumerator = G_FILE_ENUMERATOR (source_object);
+  
+  enumerator->priv->pending = FALSE;
+  enumerator->priv->closed = TRUE;
+  if (enumerator->priv->outstanding_callback)
+    (*enumerator->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (enumerator);
+}
+
+/**
+ * g_file_enumerator_close_async:
+ * @enumerator: a #GFileEnumerator.
+ * @io_priority: the io priority of the request. the io priority of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the user_data to pass to callback function 
+ * 
+ **/
+void
+g_file_enumerator_close_async (GFileEnumerator                *enumerator,
+                              int                             io_priority,
+                              GCancellable                   *cancellable,
+                              GAsyncReadyCallback             callback,
+                              gpointer                        user_data)
+{
+  GFileEnumeratorClass *class;
+
+  g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator));
+
+  if (enumerator->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (enumerator),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("File enumerator is already closed"));
+      return;
+    }
+  
+  if (enumerator->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (enumerator),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("File enumerator has outstanding operation"));
+      return;
+    }
+
+  class = G_FILE_ENUMERATOR_GET_CLASS (enumerator);
+  
+  enumerator->priv->pending = TRUE;
+  enumerator->priv->outstanding_callback = callback;
+  g_object_ref (enumerator);
+  (* class->close_async) (enumerator, io_priority, cancellable,
+                         close_async_callback_wrapper, user_data);
+}
+
+/**
+ * g_file_enumerator_close_finish:
+ * @enumerator: a #GFileEnumerator.
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: %TRUE if the close operation has finished successfully.
+ **/
+gboolean
+g_file_enumerator_close_finish (GFileEnumerator                *enumerator,
+                               GAsyncResult                   *result,
+                               GError                        **error)
+{
+  GSimpleAsyncResult *simple;
+  GFileEnumeratorClass *class;
+
+  g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+  
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  class = G_FILE_ENUMERATOR_GET_CLASS (enumerator);
+  return class->close_finish (enumerator, result, error);
+}
+
+/**
+ * g_file_enumerator_is_closed:
+ * @enumerator: a #GFileEnumerator.
+ * 
+ * Returns: %TRUE if the @enumerator is closed.
+ **/
+gboolean
+g_file_enumerator_is_closed (GFileEnumerator *enumerator)
+{
+  g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), TRUE);
+  
+  return enumerator->priv->closed;
+}
+
+/**
+ * g_file_enumerator_has_pending:
+ * @enumerator: a #GFileEnumerator.
+ * 
+ * Returns: %TRUE if the @enumerator has pending operations.
+ **/
+gboolean
+g_file_enumerator_has_pending (GFileEnumerator *enumerator)
+{
+  g_return_val_if_fail (G_IS_FILE_ENUMERATOR (enumerator), TRUE);
+  
+  return enumerator->priv->pending;
+}
+
+/**
+ * g_file_enumerator_set_pending:
+ * @enumerator: a #GFileEnumerator.
+ * @pending: a boolean value.
+ * 
+ **/
+void
+g_file_enumerator_set_pending (GFileEnumerator              *enumerator,
+                              gboolean                   pending)
+{
+  g_return_if_fail (G_IS_FILE_ENUMERATOR (enumerator));
+  
+  enumerator->priv->pending = pending;
+}
+
+typedef struct {
+  int                num_files;
+  GList             *files;
+} NextAsyncOp;
+
+static void
+next_files_thread (GSimpleAsyncResult *res,
+                  GObject *object,
+                  GCancellable *cancellable)
+{
+  NextAsyncOp *op;
+  GFileEnumeratorClass *class;
+  GError *error = NULL;
+  GFileInfo *info;
+  GFileEnumerator *enumerator;
+  int i;
+
+  enumerator = G_FILE_ENUMERATOR (object);
+  op = g_simple_async_result_get_op_res_gpointer (res);
+
+  class = G_FILE_ENUMERATOR_GET_CLASS (object);
+
+  for (i = 0; i < op->num_files; i++)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, &error))
+       info = NULL;
+      else
+       info = class->next_file (enumerator, cancellable, &error);
+      
+      if (info == NULL)
+       {
+         /* If we get an error after first file, return that on next operation */
+         if (error != NULL && i > 0)
+           {
+             if (error->domain == G_IO_ERROR &&
+                 error->code == G_IO_ERROR_CANCELLED)
+               g_error_free (error); /* Never propagate cancel errors to other call */
+             else
+               enumerator->priv->outstanding_error = error;
+             error = NULL;
+           }
+             
+         break;
+       }
+      else
+       op->files = g_list_prepend (op->files, info);
+    }
+}
+
+
+static void
+g_file_enumerator_real_next_files_async (GFileEnumerator                *enumerator,
+                                        int                             num_files,
+                                        int                             io_priority,
+                                        GCancellable                   *cancellable,
+                                        GAsyncReadyCallback             callback,
+                                        gpointer                        user_data)
+{
+  GSimpleAsyncResult *res;
+  NextAsyncOp *op;
+
+  op = g_new0 (NextAsyncOp, 1);
+
+  op->num_files = num_files;
+  op->files = NULL;
+
+  res = g_simple_async_result_new (G_OBJECT (enumerator), callback, user_data, g_file_enumerator_real_next_files_async);
+  g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+  
+  g_simple_async_result_run_in_thread (res, next_files_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GList *
+g_file_enumerator_real_next_files_finish (GFileEnumerator                *enumerator,
+                                         GAsyncResult                   *result,
+                                         GError                        **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  NextAsyncOp *op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+           g_file_enumerator_real_next_files_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+
+  return op->files;
+}
+
+static void
+close_async_thread (GSimpleAsyncResult *res,
+                   GObject *object,
+                   GCancellable *cancellable)
+{
+  GFileEnumeratorClass *class;
+  GError *error = NULL;
+  gboolean result;
+
+  /* Auto handling of cancelation disabled, and ignore
+     cancellation, since we want to close things anyway, although
+     possibly in a quick-n-dirty way. At least we never want to leak
+     open handles */
+  
+  class = G_FILE_ENUMERATOR_GET_CLASS (object);
+  result = class->close (G_FILE_ENUMERATOR (object), cancellable, &error);
+  if (!result)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+
+static void
+g_file_enumerator_real_close_async (GFileEnumerator      *enumerator,
+                                   int                   io_priority,
+                                   GCancellable         *cancellable,
+                                   GAsyncReadyCallback   callback,
+                                   gpointer              user_data)
+{
+  GSimpleAsyncResult *res;
+  
+  res = g_simple_async_result_new (G_OBJECT (enumerator),
+                                  callback,
+                                  user_data,
+                                  g_file_enumerator_real_close_async);
+
+  g_simple_async_result_set_handle_cancellation (res, FALSE);
+  
+  g_simple_async_result_run_in_thread (res,
+                                      close_async_thread,
+                                      io_priority,
+                                      cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_file_enumerator_real_close_finish      (GFileEnumerator      *enumerator,
+                                         GAsyncResult         *result,
+                                         GError              **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+           g_file_enumerator_real_close_async);
+  return TRUE;
+}
diff --git a/gio/gfileenumerator.h b/gio/gfileenumerator.h
new file mode 100644 (file)
index 0000000..1e83f88
--- /dev/null
@@ -0,0 +1,130 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_ENUMERATOR_H__
+#define __G_FILE_ENUMERATOR_H__
+
+#include <glib-object.h>
+#include <gio/gioerror.h>
+#include <gio/gcancellable.h>
+#include <gio/gfileinfo.h>
+#include <gio/gasyncresult.h>
+
+G_BEGIN_DECLS
+
+
+#define G_TYPE_FILE_ENUMERATOR         (g_file_enumerator_get_type ())
+#define G_FILE_ENUMERATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILE_ENUMERATOR, GFileEnumerator))
+#define G_FILE_ENUMERATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILE_ENUMERATOR, GFileEnumeratorClass))
+#define G_IS_FILE_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILE_ENUMERATOR))
+#define G_IS_FILE_ENUMERATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILE_ENUMERATOR))
+#define G_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILE_ENUMERATOR, GFileEnumeratorClass))
+
+
+typedef struct _GFileEnumerator         GFileEnumerator;
+typedef struct _GFileEnumeratorClass    GFileEnumeratorClass;
+typedef struct _GFileEnumeratorPrivate  GFileEnumeratorPrivate;
+
+
+struct _GFileEnumerator
+{
+  GObject parent;
+  
+  /*< private >*/
+  GFileEnumeratorPrivate *priv;
+};
+
+struct _GFileEnumeratorClass
+{
+  GObjectClass parent_class;
+
+  /* Virtual Table */
+
+  GFileInfo *(*next_file)         (GFileEnumerator              *enumerator,
+                                  GCancellable                 *cancellable,
+                                  GError                      **error);
+  gboolean   (*close)             (GFileEnumerator              *enumerator,
+                                  GCancellable                 *cancellable,
+                                  GError                      **error);
+
+  void       (*next_files_async)  (GFileEnumerator                *enumerator,
+                                  int                             num_files,
+                                  int                             io_priority,
+                                  GCancellable                   *cancellable,
+                                  GAsyncReadyCallback             callback,
+                                  gpointer                        user_data);
+  GList *    (*next_files_finish) (GFileEnumerator                *enumerator,
+                                  GAsyncResult                   *res,
+                                  GError                        **error);
+  void       (*close_async)       (GFileEnumerator                *enumerator,
+                                  int                             io_priority,
+                                  GCancellable                   *cancellable,
+                                  GAsyncReadyCallback             callback,
+                                  gpointer                        user_data);
+  gboolean   (*close_finish)      (GFileEnumerator                *enumerator,
+                                  GAsyncResult                   *res,
+                                  GError                        **error);
+
+
+    /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+};
+
+GType g_file_enumerator_get_type (void) G_GNUC_CONST;
+
+GFileInfo *g_file_enumerator_next_file         (GFileEnumerator      *enumerator,
+                                               GCancellable         *cancellable,
+                                               GError              **error);
+gboolean   g_file_enumerator_close             (GFileEnumerator      *enumerator,
+                                               GCancellable         *cancellable,
+                                               GError              **error);
+void       g_file_enumerator_next_files_async  (GFileEnumerator      *enumerator,
+                                               int                   num_files,
+                                               int                   io_priority,
+                                               GCancellable         *cancellable,
+                                               GAsyncReadyCallback   callback,
+                                               gpointer              user_data);
+GList *    g_file_enumerator_next_files_finish (GFileEnumerator      *enumerator,
+                                               GAsyncResult         *result,
+                                               GError              **error);
+void       g_file_enumerator_close_async       (GFileEnumerator      *enumerator,
+                                               int                   io_priority,
+                                               GCancellable         *cancellable,
+                                               GAsyncReadyCallback   callback,
+                                               gpointer              user_data);
+gboolean   g_file_enumerator_close_finish      (GFileEnumerator      *enumerator,
+                                               GAsyncResult         *result,
+                                               GError              **error);
+gboolean   g_file_enumerator_is_closed         (GFileEnumerator      *enumerator);
+gboolean   g_file_enumerator_has_pending       (GFileEnumerator      *enumerator);
+void       g_file_enumerator_set_pending       (GFileEnumerator      *enumerator,
+                                               gboolean              pending);
+
+G_END_DECLS
+
+#endif /* __G_FILE_ENUMERATOR_H__ */
diff --git a/gio/gfileicon.c b/gio/gfileicon.c
new file mode 100644 (file)
index 0000000..6e46e7f
--- /dev/null
@@ -0,0 +1,257 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "gfileicon.h"
+#include "gsimpleasyncresult.h"
+
+static void g_file_icon_icon_iface_init          (GIconIface          *iface);
+static void g_file_icon_loadable_icon_iface_init (GLoadableIconIface  *iface);
+static void g_file_icon_load_async               (GLoadableIcon       *icon,
+                                                 int                  size,
+                                                 GCancellable        *cancellable,
+                                                 GAsyncReadyCallback  callback,
+                                                 gpointer             user_data);
+
+struct _GFileIcon
+{
+  GObject parent_instance;
+
+  GFile *file;
+};
+
+struct _GFileIconClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GFileIcon, g_file_icon, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
+                                               g_file_icon_icon_iface_init);
+                        G_IMPLEMENT_INTERFACE (G_TYPE_LOADABLE_ICON,
+                                               g_file_icon_loadable_icon_iface_init);
+                        )
+  
+static void
+g_file_icon_finalize (GObject *object)
+{
+  GFileIcon *icon;
+
+  icon = G_FILE_ICON (object);
+
+  g_object_unref (icon->file);
+  
+  if (G_OBJECT_CLASS (g_file_icon_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_file_icon_parent_class)->finalize) (object);
+}
+
+static void
+g_file_icon_class_init (GFileIconClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_file_icon_finalize;
+}
+
+static void
+g_file_icon_init (GFileIcon *file)
+{
+}
+
+/**
+ * g_file_icon_new:
+ * @file:
+ * 
+ * Returns: 
+ **/
+GIcon *
+g_file_icon_new (GFile *file)
+{
+  GFileIcon *icon;
+
+  g_return_val_if_fail (G_IS_FILE (file), NULL);
+
+  icon = g_object_new (G_TYPE_FILE_ICON, NULL);
+  icon->file = g_object_ref (file);
+  
+  return G_ICON (icon);
+}
+
+/**
+ * g_file_icon_get_file:
+ * @icon:
+ * 
+ * Returns: 
+ **/
+GFile *
+g_file_icon_get_file (GFileIcon *icon)
+{
+  g_return_val_if_fail (G_IS_FILE_ICON (icon), NULL);
+
+  return icon->file;
+}
+
+static guint
+g_file_icon_hash (GIcon *icon)
+{
+  GFileIcon *file_icon = G_FILE_ICON (icon);
+
+  return g_file_hash (file_icon->file);
+}
+
+static gboolean
+g_file_icon_equal (GIcon *icon1,
+                  GIcon *icon2)
+{
+  GFileIcon *file1 = G_FILE_ICON (icon1);
+  GFileIcon *file2 = G_FILE_ICON (icon2);
+  
+  return g_file_equal (file1->file, file2->file);
+}
+
+
+static void
+g_file_icon_icon_iface_init (GIconIface *iface)
+{
+  iface->hash = g_file_icon_hash;
+  iface->equal = g_file_icon_equal;
+}
+
+
+static GInputStream *
+g_file_icon_load (GLoadableIcon        *icon,
+                 int                   size,
+                 char                **type,
+                 GCancellable         *cancellable,
+                 GError              **error)
+{
+  GFileInputStream *stream;
+  GFileIcon *file_icon = G_FILE_ICON (icon);
+
+  stream = g_file_read (file_icon->file,
+                       cancellable,
+                       error);
+  
+  return G_INPUT_STREAM (stream);
+}
+
+typedef struct {
+  GLoadableIcon *icon;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+} LoadData;
+
+static void
+load_data_free (LoadData *data)
+{
+  g_object_unref (data->icon);
+  g_free (data);
+}
+
+static void
+load_async_callback (GObject *source_object,
+                    GAsyncResult *res,
+                    gpointer user_data)
+{
+  GFileInputStream *stream;
+  GError *error = NULL;
+  GSimpleAsyncResult *simple;
+  LoadData *data = user_data;
+
+  stream = g_file_read_finish (G_FILE (source_object), res, &error);
+  
+  if (stream == NULL)
+    {
+      simple = g_simple_async_result_new_from_error (G_OBJECT (data->icon),
+                                                    data->callback,
+                                                    data->user_data,
+                                                    error);
+      g_error_free (error);
+    }
+  else
+    {
+      simple = g_simple_async_result_new (G_OBJECT (data->icon),
+                                         data->callback,
+                                         data->user_data,
+                                         g_file_icon_load_async);
+      
+      g_simple_async_result_set_op_res_gpointer (simple,
+                                                stream,
+                                                g_object_unref);
+  }
+
+
+  g_simple_async_result_complete (simple);
+  
+  load_data_free (data);
+}
+
+static void
+g_file_icon_load_async  (GLoadableIcon        *icon,
+                        int                   size,
+                        GCancellable         *cancellable,
+                        GAsyncReadyCallback   callback,
+                        gpointer              user_data)
+{
+  GFileIcon *file_icon = G_FILE_ICON (icon);
+  LoadData *data;
+
+  data = g_new0 (LoadData, 1);
+  data->icon = g_object_ref (icon);
+  data->callback = callback;
+  data->user_data = user_data;
+  
+  g_file_read_async (file_icon->file, 0,
+                    cancellable,
+                    load_async_callback, data);
+  
+}
+
+static GInputStream *
+g_file_icon_load_finish (GLoadableIcon        *icon,
+                        GAsyncResult         *res,
+                        char                **type,
+                        GError              **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  gpointer op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_icon_load_async);
+
+  if (type)
+    *type = NULL;
+  
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  if (op)
+    return g_object_ref (op);
+  
+  return NULL;
+}
+
+static void
+g_file_icon_loadable_icon_iface_init (GLoadableIconIface *iface)
+{
+  iface->load = g_file_icon_load;
+  iface->load_async = g_file_icon_load_async;
+  iface->load_finish = g_file_icon_load_finish;
+}
diff --git a/gio/gfileicon.h b/gio/gfileicon.h
new file mode 100644 (file)
index 0000000..1745e5f
--- /dev/null
@@ -0,0 +1,49 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_ICON_H__
+#define __G_FILE_ICON_H__
+
+#include <gio/gloadableicon.h>
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILE_ICON         (g_file_icon_get_type ())
+#define G_FILE_ICON(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILE_ICON, GFileIcon))
+#define G_FILE_ICON_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILE_ICON, GFileIconClass))
+#define G_IS_FILE_ICON(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILE_ICON))
+#define G_IS_FILE_ICON_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILE_ICON))
+#define G_FILE_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILE_ICON, GFileIconClass))
+
+typedef struct _GFileIcon        GFileIcon;
+typedef struct _GFileIconClass   GFileIconClass;
+
+GType g_file_icon_get_type (void) G_GNUC_CONST;
+  
+GIcon *g_file_icon_new (GFile *file);
+
+GFile *g_file_icon_get_file (GFileIcon *icon);
+
+G_END_DECLS
+
+#endif /* __G_FILE_ICON_H__ */
diff --git a/gio/gfileinfo.c b/gio/gfileinfo.c
new file mode 100644 (file)
index 0000000..07c8f49
--- /dev/null
@@ -0,0 +1,1924 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "gfileinfo.h"
+#include "glibintl.h"
+
+/* We use this nasty thing, because NULL is a valid attribute matcher (matches nothing) */
+#define NO_ATTRIBUTE_MASK ((GFileAttributeMatcher *)1)
+
+typedef struct  {
+  guint32 attribute;
+  GFileAttributeValue value;
+} GFileAttribute;
+
+struct _GFileInfo
+{
+  GObject parent_instance;
+
+  GArray *attributes;
+  GFileAttributeMatcher *mask;
+};
+
+struct _GFileInfoClass
+{
+  GObjectClass parent_class;
+};
+
+static gboolean g_file_attribute_matcher_matches_id (GFileAttributeMatcher *matcher,
+                                                    guint32 id);
+
+G_DEFINE_TYPE (GFileInfo, g_file_info, G_TYPE_OBJECT);
+
+typedef struct {
+  guint32 id;
+  guint32 attribute_id_counter;
+} NSInfo;
+
+G_LOCK_DEFINE_STATIC (attribute_hash);
+static int namespace_id_counter = 0;
+static GHashTable *ns_hash = NULL;
+static GHashTable *attribute_hash = NULL;
+static char ***attributes = NULL;
+
+/* Attribute ids are 32bit, we split it up like this:
+ * |------------|--------------------|
+ *   12 bit          20 bit       
+ *   namespace      attribute id    
+ *
+ * This way the attributes gets sorted in namespace order
+ */
+
+#define NS_POS 20
+#define NS_MASK ((guint32)((1<<12) - 1))
+#define ID_POS 0
+#define ID_MASK ((guint32)((1<<20) - 1))
+
+#define GET_NS(_attr_id) \
+    (((guint32) (_attr_id) >> NS_POS) & NS_MASK)
+#define GET_ID(_attr_id) \
+    (((guint32)(_attr_id) >> ID_POS) & ID_MASK)
+
+#define MAKE_ATTR_ID(_ns, _id)                         \
+    ( ((((guint32) _ns) & NS_MASK) << NS_POS) |                \
+      ((((guint32) _id) & ID_MASK) << ID_POS) )
+
+static NSInfo *
+_lookup_namespace (const char *namespace)
+{
+  NSInfo *ns_info;
+  
+  ns_info = g_hash_table_lookup (ns_hash, namespace);
+  if (ns_info == NULL)
+    {
+      ns_info = g_new0 (NSInfo, 1);
+      ns_info->id = ++namespace_id_counter;
+      g_hash_table_insert (ns_hash, g_strdup (namespace), ns_info);
+      attributes = g_realloc (attributes, (ns_info->id + 1) * sizeof (char **));
+      attributes[ns_info->id] = NULL;
+    }
+  return ns_info;
+}
+
+static guint32
+lookup_namespace (const char *namespace)
+{
+  NSInfo *ns_info;
+  guint32 id;
+  
+  G_LOCK (attribute_hash);
+  
+  if (attribute_hash == NULL)
+    {
+      ns_hash = g_hash_table_new (g_str_hash, g_str_equal);
+      attribute_hash = g_hash_table_new (g_str_hash, g_str_equal);
+    }
+
+  ns_info = _lookup_namespace (namespace);
+  id = 0;
+  if (ns_info)
+    id = ns_info->id;
+  
+  G_UNLOCK (attribute_hash);
+
+  return id;
+}
+
+static char *
+get_attribute_for_id (int attribute)
+{
+  char *s;
+  G_LOCK (attribute_hash);
+  s = attributes[GET_NS(attribute)][GET_ID(attribute)];
+  G_UNLOCK (attribute_hash);
+  return s;
+}
+
+static guint32
+lookup_attribute (const char *attribute)
+{
+  guint32 attr_id, id;
+  char *ns;
+  const char *colon;
+  NSInfo *ns_info;
+  
+  G_LOCK (attribute_hash);
+  if (attribute_hash == NULL)
+    {
+      ns_hash = g_hash_table_new (g_str_hash, g_str_equal);
+      attribute_hash = g_hash_table_new (g_str_hash, g_str_equal);
+    }
+
+  attr_id = GPOINTER_TO_UINT (g_hash_table_lookup (attribute_hash, attribute));
+
+  if (attr_id != 0)
+    {
+      G_UNLOCK (attribute_hash);
+      return attr_id;
+    }
+
+  colon = strchr (attribute, ':');
+  if (colon)
+    ns = g_strndup (attribute, colon - attribute);
+  else
+    ns = g_strdup ("");
+
+  ns_info = _lookup_namespace (ns);
+  g_free (ns);
+
+  id = ++ns_info->attribute_id_counter;
+  attributes[ns_info->id] = g_realloc (attributes[ns_info->id], (id + 1) * sizeof (char *));
+  attributes[ns_info->id][id] = g_strdup (attribute);
+  
+  attr_id = MAKE_ATTR_ID (ns_info->id, id);
+
+  g_hash_table_insert (attribute_hash, attributes[ns_info->id][id], GUINT_TO_POINTER (attr_id));
+  
+  G_UNLOCK (attribute_hash);
+  
+  return attr_id;
+}
+
+static void
+g_file_info_finalize (GObject *object)
+{
+  GFileInfo *info;
+  int i;
+  GFileAttribute *attrs;
+
+  info = G_FILE_INFO (object);
+
+  attrs = (GFileAttribute *)info->attributes->data;
+  for (i = 0; i < info->attributes->len; i++)
+    g_file_attribute_value_clear (&attrs[i].value);
+  g_array_free (info->attributes, TRUE);  
+
+  if (info->mask != NO_ATTRIBUTE_MASK)
+    g_file_attribute_matcher_unref (info->mask);
+  
+  if (G_OBJECT_CLASS (g_file_info_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_file_info_parent_class)->finalize) (object);
+}
+
+static void
+g_file_info_class_init (GFileInfoClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_file_info_finalize;
+}
+
+static void
+g_file_info_init (GFileInfo *info)
+{
+  info->mask = NO_ATTRIBUTE_MASK;
+  info->attributes = g_array_new (FALSE, FALSE,
+                                 sizeof (GFileAttribute));
+}
+
+/**
+ * g_file_info_new:
+ * 
+ * Returns: a new #GFileInfo.
+ **/
+GFileInfo *
+g_file_info_new (void)
+{
+  return g_object_new (G_TYPE_FILE_INFO, NULL);
+}
+
+/**
+ * g_file_info_copy_into:
+ * @src_info: source to copy attributes from.
+ * @dest_info: destination to copy attributes to.
+ * 
+ * Copies all of the attributes from @src_info to @dest_info.
+ **/
+void
+g_file_info_copy_into (GFileInfo *src_info, GFileInfo *dest_info)
+{
+  GFileAttribute *source, *dest;
+  int i;
+
+  g_return_if_fail (G_IS_FILE_INFO (src_info));
+  g_return_if_fail (G_IS_FILE_INFO (dest_info));
+
+  dest = (GFileAttribute *)dest_info->attributes->data;
+  for (i = 0; i < dest_info->attributes->len; i++)
+    g_file_attribute_value_clear (&dest[i].value);
+  
+  g_array_set_size (dest_info->attributes,
+                   src_info->attributes->len);
+
+  source = (GFileAttribute *)src_info->attributes->data;
+  dest = (GFileAttribute *)dest_info->attributes->data;
+  
+  for (i = 0; i < src_info->attributes->len; i++)
+    {
+      dest[i].attribute = source[i].attribute;
+      dest[i].value.type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+      g_file_attribute_value_set (&dest[i].value, &source[i].value);
+    }
+
+  if (src_info->mask == NO_ATTRIBUTE_MASK)
+    dest_info->mask = NO_ATTRIBUTE_MASK;
+  else
+    dest_info->mask = g_file_attribute_matcher_ref (src_info->mask);
+}
+
+/**
+ * g_file_info_dup:
+ * @other: a #GFileInfo.
+ * 
+ * Returns: a duplicate #GFileInfo of @other.
+ **/
+GFileInfo *
+g_file_info_dup (GFileInfo  *other)
+{
+  GFileInfo *new;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (other), NULL);
+  
+  new = g_file_info_new ();
+  g_file_info_copy_into (other, new);
+  return new;
+}
+
+/**
+ * g_file_info_set_attribute_mask:
+ * @info: a #GFileInfo.
+ * @mask: a #GFileAttributeMatcher.
+ *
+ * Sets @mask on @info to match specific attribute types.
+ * 
+ **/
+void
+g_file_info_set_attribute_mask (GFileInfo *info,
+                               GFileAttributeMatcher *mask)
+{
+  GFileAttribute *attr;
+  int i;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (mask != NULL);
+  
+  if (mask != info->mask)
+    {
+      if (info->mask != NO_ATTRIBUTE_MASK)
+       g_file_attribute_matcher_unref (info->mask);
+      info->mask = g_file_attribute_matcher_ref (mask);
+
+      /* Remove non-matching attributes */
+      for (i = 0; i < info->attributes->len; i++)
+       {
+         attr = &g_array_index (info->attributes, GFileAttribute, i);
+         if (!g_file_attribute_matcher_matches_id (mask,
+                                                   attr->attribute))
+           {
+             g_file_attribute_value_clear (&attr->value);
+             g_array_remove_index (info->attributes, i);
+             i--;
+           }
+       }
+    }
+}
+
+/**
+ * g_file_info_unset_attribute_mask:
+ * @info: #GFileInfo.
+ * 
+ **/
+void
+g_file_info_unset_attribute_mask (GFileInfo *info)
+{
+  g_return_if_fail (G_IS_FILE_INFO (info));
+
+  if (info->mask != NO_ATTRIBUTE_MASK)
+    g_file_attribute_matcher_unref (info->mask);
+  info->mask = NO_ATTRIBUTE_MASK;
+}
+
+/**
+ * g_file_info_clear_status:
+ * @info: a #GFileInfo.
+ *
+ * Clears the status information from @info.
+ * 
+ **/
+void
+g_file_info_clear_status (GFileInfo  *info)
+{
+  GFileAttribute *attrs;
+  int i;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+
+  attrs = (GFileAttribute *)info->attributes->data;
+  for (i = 0; i < info->attributes->len; i++)
+    attrs[i].value.status = G_FILE_ATTRIBUTE_STATUS_UNSET;
+}
+
+static int
+g_file_info_find_place (GFileInfo  *info,
+                       guint32 attribute)
+{
+  int min, max, med;
+  GFileAttribute *attrs;
+  /* Binary search for the place where attribute would be, if its
+     in the array */
+
+  min = 0;
+  max = info->attributes->len;
+
+  attrs = (GFileAttribute *)info->attributes->data;
+
+  while (min < max)
+    {
+      med = min + (max - min) / 2;
+      if (attrs[med].attribute == attribute)
+       {
+         min = med;
+         break;
+       }
+      else if (attrs[med].attribute < attribute)
+       min = med + 1;
+      else /* attrs[med].attribute > attribute */
+       max = med;
+    }
+
+  return min;
+}
+
+static GFileAttributeValue *
+g_file_info_find_value (GFileInfo *info,
+                       guint32 attr_id)
+{
+  GFileAttribute *attrs;
+  int i;
+
+  i = g_file_info_find_place (info, attr_id);
+  attrs = (GFileAttribute *)info->attributes->data;
+  if (i < info->attributes->len &&
+      attrs[i].attribute == attr_id)
+    return &attrs[i].value;
+  
+  return NULL;
+}
+
+static GFileAttributeValue *
+g_file_info_find_value_by_name (GFileInfo *info,
+                               const char *attribute)
+{
+  guint32 attr_id;
+
+  attr_id = lookup_attribute (attribute);
+  return g_file_info_find_value (info, attr_id);
+}
+
+/**
+ * g_file_info_has_attribute:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: %TRUE if @GFileInfo has an attribute named @attribute, 
+ * %FALSE otherwise.
+ **/
+gboolean
+g_file_info_has_attribute (GFileInfo  *info,
+                          const char *attribute)
+{
+  GFileAttributeValue *value;
+
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', FALSE);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return value != NULL;
+}
+
+/**
+ * g_file_info_list_attributes:
+ * @info: a #GFileInfo.
+ * @name_space: a string.
+ * 
+ * Returns: a null-terminated array of strings of all of the 
+ * possible attribute types for the given @name_space, or 
+ * %NULL on error.
+ **/
+char **
+g_file_info_list_attributes (GFileInfo  *info,
+                            const char *name_space)
+{
+  GPtrArray *names;
+  GFileAttribute *attrs;
+  guint32 attribute;
+  guint32 ns_id = (name_space) ? lookup_namespace (name_space) : 0;
+  int i;
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+
+  names = g_ptr_array_new ();
+  attrs = (GFileAttribute *)info->attributes->data;
+  for (i = 0; i < info->attributes->len; i++)
+    {
+      attribute = attrs[i].attribute;
+      if (ns_id == 0 || GET_NS (attribute) == ns_id)
+        g_ptr_array_add (names, g_strdup (get_attribute_for_id (attribute)));
+    }
+
+  /* NULL terminate */
+  g_ptr_array_add (names, NULL);
+  
+  return (char **)g_ptr_array_free (names, FALSE);
+}
+
+/**
+ * g_file_info_get_attribute_type:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: a #GFileAttributeType for the given @attribute, or 
+ * %G_FILE_ATTRIBUTE_TYPE_INVALID if one cannot be found.
+ **/
+GFileAttributeType
+g_file_info_get_attribute_type (GFileInfo  *info,
+                               const char *attribute)
+{
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), G_FILE_ATTRIBUTE_TYPE_INVALID);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', G_FILE_ATTRIBUTE_TYPE_INVALID);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  if (value)
+    return value->type;
+  else
+    return G_FILE_ATTRIBUTE_TYPE_INVALID;
+}
+
+/**
+ * g_file_info_remove_attribute:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Removes @attribute from @info if it exists.
+ *
+ **/
+void
+g_file_info_remove_attribute (GFileInfo  *info,
+                             const char *attribute)
+{
+  guint32 attr_id;
+  GFileAttribute *attrs;
+  int i;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+
+  attr_id = lookup_attribute (attribute);
+  
+  i = g_file_info_find_place (info, attr_id);
+  attrs = (GFileAttribute *)info->attributes->data;
+  if (i < info->attributes->len &&
+      attrs[i].attribute == attr_id)
+    {
+      g_file_attribute_value_clear (&attrs[i].value);
+      g_array_remove_index (info->attributes, i);
+    }
+}
+
+/**
+ * g_file_info_get_attribute:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: a #GFileAttributeValue for the given @attribute, or 
+ * %NULL otherwise.
+ **/
+GFileAttributeValue *
+g_file_info_get_attribute (GFileInfo  *info,
+                          const char *attribute)
+  
+{
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', NULL);
+
+  return g_file_info_find_value_by_name (info, attribute);
+}
+
+/**
+ * g_file_info_get_attribute_object:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: a #GObject associated with the given @attribute, or
+ * %NULL otherwise.
+ **/
+GObject *
+g_file_info_get_attribute_object (GFileInfo  *info,
+                                 const char *attribute)
+{
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', NULL);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_object (value);
+}
+
+/**
+ * g_file_info_get_attribute_string:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: the contents of the @attribute value as a string, or 
+ * %NULL otherwise.
+ **/
+const char *
+g_file_info_get_attribute_string (GFileInfo  *info,
+                                 const char *attribute)
+{
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', NULL);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_string (value);
+}
+
+/**
+ * g_file_info_get_attribute_byte_string:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: the contents of the @attribute value as a byte string, or 
+ * %NULL otherwise.
+ **/
+const char *
+g_file_info_get_attribute_byte_string (GFileInfo  *info,
+                                      const char *attribute)
+{
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', NULL);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_byte_string (value);
+}
+
+/**
+ * g_file_info_get_attribute_boolean:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: 
+ **/
+gboolean
+g_file_info_get_attribute_boolean (GFileInfo  *info,
+                                  const char *attribute)
+{
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', FALSE);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_boolean (value);
+}
+
+/**
+ * g_file_info_get_attribute_uint32:
+ * @info: a #GFileInfo.
+ * @attribute: a string.
+ * 
+ * Returns: 
+ **/
+guint32
+g_file_info_get_attribute_uint32 (GFileInfo  *info,
+                                 const char *attribute)
+{
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), 0);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', 0);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_uint32 (value);
+}
+
+/**
+ * g_file_info_get_attribute_int32:
+ * @info:
+ * @attribute:
+ * 
+ * Returns:
+ **/
+gint32
+g_file_info_get_attribute_int32 (GFileInfo  *info,
+                                const char *attribute)
+{
+  GFileAttributeValue *value;
+
+  g_return_val_if_fail (G_IS_FILE_INFO (info), 0);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', 0);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_int32 (value);
+}
+
+/**
+ * g_file_info_get_attribute_uint64:
+ * @info:
+ * @attribute:
+ * 
+ * Returns: 
+ **/
+guint64
+g_file_info_get_attribute_uint64 (GFileInfo  *info,
+                                 const char *attribute)
+{
+  GFileAttributeValue *value;
+
+  g_return_val_if_fail (G_IS_FILE_INFO (info), 0);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', 0);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_uint64 (value);
+}
+
+/**
+ * g_file_info_get_attribute_int64:
+ * @info:
+ * @attribute:
+ * 
+ * Returns: 
+ **/
+gint64
+g_file_info_get_attribute_int64  (GFileInfo  *info,
+                                 const char *attribute)
+{
+  GFileAttributeValue *value;
+
+  g_return_val_if_fail (G_IS_FILE_INFO (info), 0);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', 0);
+
+  value = g_file_info_find_value_by_name (info, attribute);
+  return g_file_attribute_value_get_int64 (value);
+}
+
+static GFileAttributeValue *
+g_file_info_create_value (GFileInfo *info,
+                         guint32 attr_id)
+{
+  GFileAttribute *attrs;
+  GFileAttribute attr;
+  int i;
+
+  if (info->mask != NO_ATTRIBUTE_MASK &&
+      !g_file_attribute_matcher_matches_id (info->mask, attr_id))
+    return NULL;
+  
+  i = g_file_info_find_place (info, attr_id);
+  
+  attrs = (GFileAttribute *)info->attributes->data;
+  if (i < info->attributes->len &&
+      attrs[i].attribute == attr_id)
+    return &attrs[i].value;
+  else
+    {
+      attr.attribute = attr_id;
+      attr.value.type = G_FILE_ATTRIBUTE_TYPE_INVALID;
+      g_array_insert_val (info->attributes, i, attr);
+
+      attrs = (GFileAttribute *)info->attributes->data;
+      return &attrs[i].value;
+    }
+}
+
+static GFileAttributeValue *
+g_file_info_create_value_by_name (GFileInfo *info,
+                                 const char *attribute)
+{
+  guint32 attr_id;
+
+  attr_id = lookup_attribute (attribute);
+
+  return g_file_info_create_value (info, attr_id);
+}
+
+/**
+ * g_file_info_set_attribute:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ *
+ **/
+void
+g_file_info_set_attribute (GFileInfo  *info,
+                          const char *attribute,
+                          const GFileAttributeValue *attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+  g_return_if_fail (attr_value != NULL);
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_object:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ * 
+ **/
+void
+g_file_info_set_attribute_object (GFileInfo  *info,
+                                 const char *attribute,
+                                 GObject *attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+  g_return_if_fail (G_IS_OBJECT (attr_value));
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_object (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_string:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ *
+ **/
+void
+g_file_info_set_attribute_string (GFileInfo  *info,
+                                 const char *attribute,
+                                 const char *attr_value)
+{
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+  g_return_if_fail (attr_value != NULL);
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_string (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_byte_string:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ * 
+ **/
+void
+g_file_info_set_attribute_byte_string (GFileInfo  *info,
+                                      const char *attribute,
+                                      const char *attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+  g_return_if_fail (attr_value != NULL);
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_byte_string (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_boolean:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ * 
+ **/
+void
+g_file_info_set_attribute_boolean (GFileInfo  *info,
+                                  const char *attribute,
+                                  gboolean attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_boolean (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_uint32:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ * 
+ **/
+
+void
+g_file_info_set_attribute_uint32 (GFileInfo  *info,
+                                 const char *attribute,
+                                 guint32     attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_uint32 (value, attr_value);
+}
+
+
+/**
+ * g_file_info_set_attribute_int32:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ * 
+ **/
+void
+g_file_info_set_attribute_int32  (GFileInfo  *info,
+                                 const char *attribute,
+                                 gint32      attr_value)
+{
+  GFileAttributeValue *value;
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_int32 (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_uint64:
+ * @info:
+ * @attribute:
+ * @attr_value:
+ * 
+ **/
+void
+g_file_info_set_attribute_uint64 (GFileInfo  *info,
+                                 const char *attribute,
+                                 guint64     attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_uint64 (value, attr_value);
+}
+
+/**
+ * g_file_info_set_attribute_int64:
+ * @info:
+ * @attribute: attribute name to set.
+ * @attr_value: int64 value to set attribute to.
+ * 
+ **/
+void
+g_file_info_set_attribute_int64  (GFileInfo  *info,
+                                 const char *attribute,
+                                 gint64      attr_value)
+{
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (attribute != NULL && *attribute != '\0');
+
+  value = g_file_info_create_value_by_name (info, attribute);
+  if (value)
+    g_file_attribute_value_set_int64 (value, attr_value);
+}
+
+/* Helper getters */
+/**
+ * g_file_info_get_file_type:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: a #GFileType for the given file.
+ **/
+GFileType
+g_file_info_get_file_type (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+
+  g_return_val_if_fail (G_IS_FILE_INFO (info), G_FILE_TYPE_UNKNOWN);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_TYPE);
+  
+  value = g_file_info_find_value (info, attr);
+  return (GFileType)g_file_attribute_value_get_uint32 (value);
+}
+
+/**
+ * g_file_info_get_is_hidden:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: %TRUE if the file is a hidden file, %FALSE otherwise.
+ **/
+gboolean
+g_file_info_get_is_hidden (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_IS_HIDDEN);
+  
+  value = g_file_info_find_value (info, attr);
+  return (GFileType)g_file_attribute_value_get_boolean (value);
+}
+
+/**
+ * g_file_info_get_is_backup:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: %TRUE if file is a backup file (.*~), %FALSE otherwise.
+ **/
+gboolean
+g_file_info_get_is_backup (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_IS_BACKUP);
+  
+  value = g_file_info_find_value (info, attr);
+  return (GFileType)g_file_attribute_value_get_boolean (value);
+}
+
+/**
+ * g_file_info_get_is_symlink:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: %TRUE if the given @info is a symlink.
+ **/
+gboolean
+g_file_info_get_is_symlink (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), FALSE);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_IS_SYMLINK);
+  
+  value = g_file_info_find_value (info, attr);
+  return (GFileType)g_file_attribute_value_get_boolean (value);
+}
+
+/**
+ * g_file_info_get_name:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_info_get_name (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_NAME);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_byte_string (value);
+}
+
+/**
+ * g_file_info_get_display_name:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_info_get_display_name (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_DISPLAY_NAME);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_string (value);
+}
+
+/**
+ * g_file_info_get_edit_name:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_info_get_edit_name (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_EDIT_NAME);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_string (value);
+}
+
+/**
+ * g_file_info_get_icon:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: #GIcon for the given @info.
+ **/
+GIcon *
+g_file_info_get_icon (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  GObject *obj;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_ICON);
+  
+  value = g_file_info_find_value (info, attr);
+  obj = g_file_attribute_value_get_object (value);
+  if (obj != NULL && G_IS_ICON (obj))
+    return G_ICON (obj);
+  return NULL;
+}
+
+/**
+ * g_file_info_get_content_type:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_info_get_content_type (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_CONTENT_TYPE);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_string (value);
+}
+
+/**
+ * g_file_info_get_size:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: goffset. 
+ **/
+goffset
+g_file_info_get_size (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  g_return_val_if_fail (G_IS_FILE_INFO (info), (goffset) 0);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_SIZE);
+  
+  value = g_file_info_find_value (info, attr);
+  return (goffset) g_file_attribute_value_get_uint64 (value);
+}
+
+/**
+ * g_file_info_get_modification_time:
+ * @info: a #GFileInfo.
+ * @result:
+ * 
+ **/
+
+void
+g_file_info_get_modification_time (GFileInfo *info,
+                                  GTimeVal  *result)
+{
+  static guint32 attr_mtime = 0, attr_mtime_usec;
+  GFileAttributeValue *value;
+
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (result != NULL);
+  
+  if (attr_mtime == 0)
+    {
+      attr_mtime = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED);
+      attr_mtime_usec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+    }
+  
+  value = g_file_info_find_value (info, attr_mtime);
+  result->tv_sec = g_file_attribute_value_get_uint64 (value);
+  value = g_file_info_find_value (info, attr_mtime_usec);
+  result->tv_usec = g_file_attribute_value_get_uint32 (value);
+}
+
+/**
+ * g_file_info_get_symlink_target:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_info_get_symlink_target (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_byte_string (value);
+}
+
+/**
+ * g_file_info_get_etag:
+ * @info: a #GFileInfo.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_info_get_etag (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_ETAG_VALUE);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_string (value);
+}
+
+/**
+ * g_file_info_get_sort_order:
+ * @info: a #GFileInfo.
+ * 
+ * Returns:
+ **/
+gint32
+g_file_info_get_sort_order (GFileInfo *info)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_val_if_fail (G_IS_FILE_INFO (info), 0);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_SORT_ORDER);
+  
+  value = g_file_info_find_value (info, attr);
+  return g_file_attribute_value_get_int32 (value);
+}
+
+/* Helper setters: */
+/**
+ * g_file_info_set_file_type:
+ * @info: a #GFileInfo.
+ * @type:
+ *  
+ **/
+void
+g_file_info_set_file_type (GFileInfo         *info,
+                          GFileType          type)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_TYPE);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_uint32 (value, type);
+}
+
+/**
+ * g_file_info_set_is_hidden:
+ * @info: a #GFileInfo.
+ * @is_hidden:
+ * 
+ **/
+void
+g_file_info_set_is_hidden (GFileInfo *info,
+                          gboolean   is_hidden)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_IS_HIDDEN);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_boolean (value, is_hidden);
+}
+
+/**
+ * g_file_info_set_is_symlink:
+ * @info: a #GFileInfo.
+ * @is_symlink:
+ * 
+ **/
+void
+g_file_info_set_is_symlink (GFileInfo *info,
+                           gboolean   is_symlink)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_IS_SYMLINK);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_boolean (value, is_symlink);
+}
+
+/**
+ * g_file_info_set_name:
+ * @info: a #GFileInfo.
+ * @name:
+ * 
+ **/
+void
+g_file_info_set_name (GFileInfo         *info,
+                     const char        *name)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (name != NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_NAME);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_byte_string (value, name);
+}
+
+/**
+ * g_file_info_set_display_name:
+ * @info: a #GFileInfo.
+ * @display_name:
+ *  
+ **/
+void
+g_file_info_set_display_name (GFileInfo         *info,
+                             const char        *display_name)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (display_name != NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_DISPLAY_NAME);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_string (value, display_name);
+}
+
+/**
+ * g_file_info_set_edit_name:
+ * @info: a #GFileInfo.
+ * @edit_name:
+ * 
+ **/
+
+void
+g_file_info_set_edit_name (GFileInfo         *info,
+                          const char        *edit_name)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (edit_name != NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_EDIT_NAME);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_string (value, edit_name);
+}
+
+/**
+ * g_file_info_set_icon:
+ * @info: a #GFileInfo.
+ * @icon:
+ *
+ **/
+void
+g_file_info_set_icon (GFileInfo   *info,
+                     GIcon       *icon)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (G_IS_ICON (icon));
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_ICON);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_object (value, G_OBJECT (icon));
+}
+
+/**
+ * g_file_info_set_content_type:
+ * @info: a #GFileInfo.
+ * @content_type:
+ *
+ **/
+void
+g_file_info_set_content_type (GFileInfo         *info,
+                             const char        *content_type)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (content_type != NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_CONTENT_TYPE);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_string (value, content_type);
+}
+
+/**
+ * g_file_info_set_size:
+ * @info: a #GFileInfo.
+ * @size:
+ * 
+ **/
+void
+g_file_info_set_size (GFileInfo         *info,
+                     goffset            size)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_SIZE);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_uint64 (value, size);
+}
+
+/**
+ * g_file_info_set_modification_time
+ * @info: a #GFileInfo.
+ * @mtime:
+ * 
+ **/
+void
+g_file_info_set_modification_time (GFileInfo         *info,
+                                  GTimeVal          *mtime)
+{
+  static guint32 attr_mtime = 0, attr_mtime_usec;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (mtime != NULL);
+  
+  if (attr_mtime == 0)
+    {
+      attr_mtime = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED);
+      attr_mtime_usec = lookup_attribute (G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+    }
+  
+  value = g_file_info_create_value (info, attr_mtime);
+  if (value)
+    g_file_attribute_value_set_uint64 (value, mtime->tv_sec);
+  value = g_file_info_create_value (info, attr_mtime_usec);
+  if (value)
+    g_file_attribute_value_set_uint32 (value, mtime->tv_usec);
+}
+
+/**
+ * g_file_info_set_symlink_target:
+ * @info: a #GFileInfo.
+ * @symlink_target:
+ * 
+ **/
+void
+g_file_info_set_symlink_target (GFileInfo         *info,
+                               const char        *symlink_target)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  g_return_if_fail (symlink_target != NULL);
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_byte_string (value, symlink_target);
+}
+
+/**
+ * g_file_info_set_sort_order:
+ * @info: a #GFileInfo.
+ * @sort_order:
+ * 
+ **/
+void
+g_file_info_set_sort_order (GFileInfo         *info,
+                           gint32             sort_order)
+{
+  static guint32 attr = 0;
+  GFileAttributeValue *value;
+  
+  g_return_if_fail (G_IS_FILE_INFO (info));
+  
+  if (attr == 0)
+    attr = lookup_attribute (G_FILE_ATTRIBUTE_STD_SORT_ORDER);
+  
+  value = g_file_info_create_value (info, attr);
+  if (value)
+    g_file_attribute_value_set_int32 (value, sort_order);
+}
+
+
+#define KILOBYTE_FACTOR 1024.0
+#define MEGABYTE_FACTOR (1024.0 * 1024.0)
+#define GIGABYTE_FACTOR (1024.0 * 1024.0 * 1024.0)
+
+char *
+g_format_file_size_for_display (goffset size)
+{
+  if (size < (goffset) KILOBYTE_FACTOR)
+    return g_strdup_printf (dngettext(GETTEXT_PACKAGE, "%u byte", "%u bytes",(guint) size), (guint) size);
+  else
+    {
+      gdouble displayed_size;
+      
+      if (size < (goffset) MEGABYTE_FACTOR)
+       {
+         displayed_size = (gdouble) size / KILOBYTE_FACTOR;
+         return g_strdup_printf (_("%.1f KB"), displayed_size);
+       }
+      else if (size < (goffset) GIGABYTE_FACTOR)
+       {
+         displayed_size = (gdouble) size / MEGABYTE_FACTOR;
+         return g_strdup_printf (_("%.1f MB"), displayed_size);
+       }
+      else
+       {
+         displayed_size = (gdouble) size / GIGABYTE_FACTOR;
+         return g_strdup_printf (_("%.1f GB"), displayed_size);
+       }
+    }
+}
+
+#define ON_STACK_MATCHERS 5
+
+typedef struct {
+  guint32 id;
+  guint32 mask;
+} SubMatcher;
+
+struct _GFileAttributeMatcher {
+  gboolean all;
+  SubMatcher sub_matchers[ON_STACK_MATCHERS];
+  GArray *more_sub_matchers;
+
+  /* Interator */
+  guint32 iterator_ns;
+  int iterator_pos;
+  int ref;
+};
+
+static void
+matcher_add (GFileAttributeMatcher *matcher,
+            guint id, guint mask)
+{
+  SubMatcher *sub_matchers;
+  int i;
+  SubMatcher s;
+
+  for (i = 0; i < ON_STACK_MATCHERS; i++)
+    {
+      /* First empty spot, not found, use this */
+      if (matcher->sub_matchers[i].id == 0)
+       {
+         matcher->sub_matchers[i].id = id;
+         matcher->sub_matchers[i].mask = mask;
+         return;
+       }
+      
+      /* Already added */
+      if (matcher->sub_matchers[i].id == id &&
+         matcher->sub_matchers[i].mask == mask)
+       return;
+    }
+
+  if (matcher->more_sub_matchers == NULL)
+    matcher->more_sub_matchers = g_array_new (FALSE, FALSE, sizeof (SubMatcher));
+      
+  sub_matchers = (SubMatcher *)matcher->more_sub_matchers->data;
+  for (i = 0; i < matcher->more_sub_matchers->len; i++)
+    {
+      /* Already added */
+      if (sub_matchers[i].id == id &&
+         sub_matchers[i].mask == mask)
+       return;
+    }
+
+  s.id = id;
+  s.mask = mask;
+  
+  g_array_append_val (matcher->more_sub_matchers, s);
+}
+
+/**
+ * g_file_attribute_matcher_new
+ * @attributes:
+ * 
+ * Returns: #GFileAttributeMatcher.
+ **/
+GFileAttributeMatcher *
+g_file_attribute_matcher_new (const char *attributes)
+{
+  char **split;
+  char *colon;
+  int i;
+  GFileAttributeMatcher *matcher;
+
+  if (attributes == NULL || *attributes == '\0')
+    return NULL;
+
+  matcher = g_malloc0 (sizeof (GFileAttributeMatcher));
+  matcher->ref = 1;
+
+  split = g_strsplit (attributes, ",", -1);
+
+  for (i = 0; split[i] != NULL; i++)
+    {
+      if (strcmp (split[i], "*") == 0)
+       matcher->all = TRUE;
+      else
+       {
+         guint32 id, mask;
+  
+         colon = strchr (split[i], ':');
+         if (colon != NULL &&
+             !(colon[1] == 0 ||
+               (colon[1] == '*' &&
+                colon[2] == 0)))
+           {
+             id = lookup_attribute (split[i]);
+             mask = 0xffffffff;
+           }
+         else
+           {
+             if (colon)
+               *colon = 0;
+
+             id = lookup_namespace (split[i]) << NS_POS;
+             mask = NS_MASK << NS_POS;
+           }
+         
+         matcher_add (matcher, id, mask);
+       }
+    }
+
+  g_strfreev (split);
+
+  return matcher;
+}
+
+/**
+ * g_file_attribute_matcher_ref:
+ * @matcher: a #GFileAttributeMatcher.
+ * 
+ * Returns: a #GFileAttributeMatcher.
+ **/
+GFileAttributeMatcher *
+g_file_attribute_matcher_ref (GFileAttributeMatcher *matcher)
+{
+  g_return_val_if_fail (matcher != NULL, NULL);
+  g_return_val_if_fail (matcher->ref > 0, NULL);
+
+  g_atomic_int_inc (&matcher->ref);
+  
+  return matcher;
+}
+
+/**
+ * g_file_attribute_matcher_unref:
+ * @matcher: a #GFileAttributeMatcher.
+ * 
+ * Unreferences @matcher. If the reference count falls below 1, 
+ * the @matcher is automatically freed.
+ * 
+ **/
+void
+g_file_attribute_matcher_unref (GFileAttributeMatcher *matcher)
+{
+  g_return_if_fail (matcher != NULL);
+  g_return_if_fail (matcher->ref > 0);
+
+  if (g_atomic_int_dec_and_test (&matcher->ref))
+    {
+      if (matcher->more_sub_matchers)
+       g_array_free (matcher->more_sub_matchers, TRUE);
+      
+      g_free (matcher);
+    }
+}
+
+/**
+ * g_file_attribute_matcher_matches_only:
+ * @matcher: a #GFileAttributeMatcher.
+ * @attribute:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_file_attribute_matcher_matches_only (GFileAttributeMatcher *matcher,
+                                      const char            *attribute)
+{
+  guint32 id;
+
+  g_return_val_if_fail (matcher != NULL, FALSE);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', FALSE);
+
+  if (matcher->all)
+    return FALSE;
+  
+  id = lookup_attribute (attribute);
+
+  if (matcher->sub_matchers[0].id != 0 &&
+      matcher->sub_matchers[1].id == 0 &&
+      matcher->sub_matchers[0].mask == 0xffffffff &&
+      matcher->sub_matchers[0].id == id)
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+matcher_matches_id (GFileAttributeMatcher *matcher,
+                   guint32 id)
+{
+  SubMatcher *sub_matchers;
+  int i;
+  
+  for (i = 0; i < ON_STACK_MATCHERS; i++)
+    {
+      if (matcher->sub_matchers[i].id == 0)
+       return FALSE;
+      
+      if (matcher->sub_matchers[i].id == (id & matcher->sub_matchers[i].mask))
+       return TRUE;
+    }
+
+  if (matcher->more_sub_matchers)
+    {
+      sub_matchers = (SubMatcher *)matcher->more_sub_matchers->data;
+      for (i = 0; i < matcher->more_sub_matchers->len; i++)
+       {
+         if (sub_matchers[i].id == (id & sub_matchers[i].mask))
+           return TRUE;
+       }
+    }
+  
+  return FALSE;
+}
+
+static gboolean
+g_file_attribute_matcher_matches_id (GFileAttributeMatcher *matcher,
+                                    guint32 id)
+{
+  g_return_val_if_fail (matcher != NULL, FALSE);
+  
+  if (matcher->all)
+    return TRUE;
+  
+  return matcher_matches_id (matcher, id);
+}
+
+/**
+ * g_file_attribute_matcher_matches:
+ * @matcher: a #GFileAttributeMatcher.
+ * @attribute:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_file_attribute_matcher_matches (GFileAttributeMatcher *matcher,
+                                 const char            *attribute)
+{
+  g_return_val_if_fail (matcher != NULL, FALSE);
+  g_return_val_if_fail (attribute != NULL && *attribute != '\0', FALSE);
+
+  if (matcher->all)
+    return TRUE;
+  
+  return matcher_matches_id (matcher, lookup_attribute (attribute));
+}
+
+/* return TRUE -> all */
+/**
+ * g_file_attribute_matcher_enumerate_namespace:
+ * @matcher: a #GFileAttributeMatcher.
+ * @ns:
+ * 
+ * Returns: %TRUE, %FALSE.
+ **/
+gboolean
+g_file_attribute_matcher_enumerate_namespace (GFileAttributeMatcher *matcher,
+                                             const char            *ns)
+{
+  SubMatcher *sub_matchers;
+  int ns_id;
+  int i;
+  
+  g_return_val_if_fail (matcher != NULL, FALSE);
+  g_return_val_if_fail (ns != NULL && *ns != '\0', FALSE);
+  
+  if (matcher->all)
+    return TRUE;
+
+  ns_id = lookup_namespace (ns) << NS_POS;
+
+  for (i = 0; i < ON_STACK_MATCHERS; i++)
+    {
+      if (matcher->sub_matchers[i].id == ns_id)
+       return TRUE;
+    }
+
+  if (matcher->more_sub_matchers)
+    {
+      sub_matchers = (SubMatcher *)matcher->more_sub_matchers->data;
+      for (i = 0; i < matcher->more_sub_matchers->len; i++)
+       {
+         if (sub_matchers[i].id == ns_id)
+           return TRUE;
+       }
+    }
+
+  matcher->iterator_ns = ns_id;
+  matcher->iterator_pos = 0;
+  
+  return FALSE;
+}
+
+/**
+ * g_file_attribute_matcher_enumerate_next:
+ * @matcher: a #GFileAttributeMatcher.
+ * 
+ * Returns: 
+ **/
+const char *
+g_file_attribute_matcher_enumerate_next (GFileAttributeMatcher *matcher)
+{
+  int i;
+  SubMatcher *sub_matcher;
+  
+  g_return_val_if_fail (matcher != NULL, NULL);
+
+  while (1)
+    {
+      i = matcher->iterator_pos++;
+
+      if (i < ON_STACK_MATCHERS)
+       {
+         if (matcher->sub_matchers[i].id == 0)
+           return NULL;
+
+         sub_matcher = &matcher->sub_matchers[i];
+       }
+      else
+       {
+         if (matcher->more_sub_matchers == NULL)
+           return NULL;
+      
+         i -= ON_STACK_MATCHERS;
+         if (i < matcher->more_sub_matchers->len)
+           sub_matcher = &g_array_index (matcher->more_sub_matchers, SubMatcher, i);
+         else
+           return NULL;
+       }
+
+      if (sub_matcher->mask == 0xffffffff &&
+         (sub_matcher->id & (NS_MASK << NS_POS)) == matcher->iterator_ns)
+       return get_attribute_for_id (sub_matcher->id);
+    }
+}
diff --git a/gio/gfileinfo.h b/gio/gfileinfo.h
new file mode 100644 (file)
index 0000000..480d6a6
--- /dev/null
@@ -0,0 +1,280 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_INFO_H__
+#define __G_FILE_INFO_H__
+
+#include <glib-object.h>
+#include <gio/gfileattribute.h>
+#include <gio/gicon.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILE_INFO         (g_file_info_get_type ())
+#define G_FILE_INFO(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILE_INFO, GFileInfo))
+#define G_FILE_INFO_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILE_INFO, GFileInfoClass))
+#define G_IS_FILE_INFO(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILE_INFO))
+#define G_IS_FILE_INFO_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILE_INFO))
+#define G_FILE_INFO_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILE_INFO, GFileInfoClass))
+
+typedef struct _GFileInfo        GFileInfo;
+typedef struct _GFileInfoClass   GFileInfoClass;
+typedef struct _GFileAttributeMatcher GFileAttributeMatcher;
+
+typedef enum {
+  G_FILE_TYPE_UNKNOWN = 0,
+  G_FILE_TYPE_REGULAR,
+  G_FILE_TYPE_DIRECTORY,
+  G_FILE_TYPE_SYMBOLIC_LINK,
+  G_FILE_TYPE_SPECIAL, /* socket, fifo, blockdev, chardev */
+  G_FILE_TYPE_SHORTCUT,
+  G_FILE_TYPE_MOUNTABLE
+} GFileType;
+
+/* Common Attributes:  */
+
+#define G_FILE_ATTRIBUTE_STD_TYPE "std:type"                     /* uint32 (GFileType) */
+#define G_FILE_ATTRIBUTE_STD_IS_HIDDEN "std:is_hidden"           /* boolean */
+#define G_FILE_ATTRIBUTE_STD_IS_BACKUP "std:is_backup"           /* boolean */
+#define G_FILE_ATTRIBUTE_STD_IS_SYMLINK "std:is_symlink"         /* boolean */
+#define G_FILE_ATTRIBUTE_STD_IS_VIRTUAL "std:is_virtual"         /* boolean */
+#define G_FILE_ATTRIBUTE_STD_NAME "std:name"                     /* byte string */
+#define G_FILE_ATTRIBUTE_STD_DISPLAY_NAME "std:display_name"     /* string */
+#define G_FILE_ATTRIBUTE_STD_EDIT_NAME "std:edit_name"           /* string */
+#define G_FILE_ATTRIBUTE_STD_COPY_NAME "std:copy_name"           /* string */
+#define G_FILE_ATTRIBUTE_STD_ICON "std:icon"                     /* object (GIcon) */
+#define G_FILE_ATTRIBUTE_STD_CONTENT_TYPE "std:content_type"     /* string */
+#define G_FILE_ATTRIBUTE_STD_FAST_CONTENT_TYPE "std:fast_content_type" /* string */
+#define G_FILE_ATTRIBUTE_STD_SIZE "std:size"                     /* uint64 */
+#define G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET "std:symlink_target" /* byte string */
+#define G_FILE_ATTRIBUTE_STD_TARGET_URI "std:target_uri"         /* string */
+#define G_FILE_ATTRIBUTE_STD_SORT_ORDER "std:sort_order"         /* int32  */
+
+/* Entity tags, used to avoid missing updates on save */
+
+#define G_FILE_ATTRIBUTE_ETAG_VALUE "etag:value"                 /* string */
+
+/* File identifier, for e.g. avoiding loops when doing recursive directory scanning */
+
+#define G_FILE_ATTRIBUTE_ID_FILE "id:file"                     /* string */
+#define G_FILE_ATTRIBUTE_ID_FS "id:fs"                         /* string */
+
+/* Calculated Access Rights for current user */
+
+#define G_FILE_ATTRIBUTE_ACCESS_CAN_READ "access:can_read"       /* boolean */
+#define G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE "access:can_write"     /* boolean */
+#define G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE "access:can_execute" /* boolean */
+#define G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE "access:can_delete"   /* boolean */
+#define G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH "access:can_trash"     /* boolean */
+#define G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME "access:can_rename"   /* boolean */ 
+/* TODO: Should we have special version for directories? can_enumerate, etc */
+
+/* Mountable attributes */
+
+#define G_FILE_ATTRIBUTE_MOUNTABLE_CAN_MOUNT "mountable:can_mount"     /* boolean */
+#define G_FILE_ATTRIBUTE_MOUNTABLE_CAN_UNMOUNT "mountable:can_unmount" /* boolean */
+#define G_FILE_ATTRIBUTE_MOUNTABLE_CAN_EJECT "mountable:can_eject"     /* boolean */
+#define G_FILE_ATTRIBUTE_MOUNTABLE_UNIX_DEVICE "mountable:unix_device" /* uint32 */
+#define G_FILE_ATTRIBUTE_MOUNTABLE_HAL_UDI "mountable:hal_udi"         /* string */
+
+/* Time attributes */
+
+  /* The last time the file content or an attribute was modified */
+#define G_FILE_ATTRIBUTE_TIME_MODIFIED "time:modified"           /* uint64 */
+#define G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC "time:modified_usec" /* uint32 */
+  /* The last time the file was read */
+#define G_FILE_ATTRIBUTE_TIME_ACCESS "time:access"               /* uint64 */
+#define G_FILE_ATTRIBUTE_TIME_ACCESS_USEC "time:access_usec"     /* uint32 */
+  /* The last time a file attribute was changed (e.g. unix ctime) */
+#define G_FILE_ATTRIBUTE_TIME_CHANGED "time:changed"             /* uint64 */
+#define G_FILE_ATTRIBUTE_TIME_CHANGED_USEC "time:changed_usec"   /* uint32 */
+  /* When the file was originally created (e.g. ntfs ctime) */
+#define G_FILE_ATTRIBUTE_TIME_CREATED "time:created"             /* uint64 */
+#define G_FILE_ATTRIBUTE_TIME_CREATED_USEC "time:created_usec"   /* uint32 */
+
+/* Unix specific attributes */
+
+#define G_FILE_ATTRIBUTE_UNIX_DEVICE "unix:device"               /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_INODE "unix:inode"                 /* uint64 */
+#define G_FILE_ATTRIBUTE_UNIX_MODE "unix:mode"                   /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_NLINK "unix:nlink"                 /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_UID "unix:uid"                     /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_GID "unix:gid"                     /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_RDEV "unix:rdev"                   /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE "unix:block_size"       /* uint32 */
+#define G_FILE_ATTRIBUTE_UNIX_BLOCKS "unix:blocks"               /* uint64 */
+#define G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT "unix:is_mountpoint" /* boolean */
+
+/* DOS specific attributes */
+
+#define G_FILE_ATTRIBUTE_DOS_IS_ARCHIVE "dos:is_archive"         /* boolean */
+#define G_FILE_ATTRIBUTE_DOS_IS_SYSTEM "dos:is_system"           /* boolean */
+
+/* Owner attributes */
+
+#define G_FILE_ATTRIBUTE_OWNER_USER "owner:user"                 /* string */
+#define G_FILE_ATTRIBUTE_OWNER_USER_REAL "owner:user_real"       /* string */
+#define G_FILE_ATTRIBUTE_OWNER_GROUP "owner:group"               /* string */
+
+/* Thumbnails */
+
+#define G_FILE_ATTRIBUTE_THUMBNAIL_PATH "thumbnail:path"         /* bytestring */
+#define G_FILE_ATTRIBUTE_THUMBNAILING_FAILED "thumbnail:failed"         /* bytestring */
+
+/* File system info (for g_file_get_filesystem_info) */
+
+#define G_FILE_ATTRIBUTE_FS_SIZE "fs:size"                       /* uint64 */
+#define G_FILE_ATTRIBUTE_FS_FREE "fs:free"                       /* uint64 */
+#define G_FILE_ATTRIBUTE_FS_TYPE "fs:type"                       /* string */
+#define G_FILE_ATTRIBUTE_FS_READONLY "fs:readonly"               /* boolean */
+
+#define G_FILE_ATTRIBUTE_GVFS_BACKEND "gvfs:backend"             /* string */
+
+GType g_file_info_get_type (void) G_GNUC_CONST;
+
+GFileInfo *        g_file_info_new                       (void);
+GFileInfo *        g_file_info_dup                       (GFileInfo  *other);
+void               g_file_info_copy_into                 (GFileInfo  *src_info,
+                                                         GFileInfo  *dest_info);
+gboolean           g_file_info_has_attribute             (GFileInfo  *info,
+                                                         const char *attribute);
+char **            g_file_info_list_attributes           (GFileInfo  *info,
+                                                         const char *name_space);
+GFileAttributeType g_file_info_get_attribute_type        (GFileInfo  *info,
+                                                         const char *attribute);
+void               g_file_info_remove_attribute          (GFileInfo  *info,
+                                                         const char *attribute);
+GFileAttributeValue * g_file_info_get_attribute          (GFileInfo  *info,
+                                                         const char *attribute);
+const char *       g_file_info_get_attribute_string      (GFileInfo  *info,
+                                                         const char *attribute);
+const char *       g_file_info_get_attribute_byte_string (GFileInfo  *info,
+                                                         const char *attribute);
+gboolean           g_file_info_get_attribute_boolean     (GFileInfo  *info,
+                                                         const char *attribute);
+guint32            g_file_info_get_attribute_uint32      (GFileInfo  *info,
+                                                         const char *attribute);
+gint32             g_file_info_get_attribute_int32       (GFileInfo  *info,
+                                                         const char *attribute);
+guint64            g_file_info_get_attribute_uint64      (GFileInfo  *info,
+                                                         const char *attribute);
+gint64             g_file_info_get_attribute_int64       (GFileInfo  *info,
+                                                         const char *attribute);
+GObject *          g_file_info_get_attribute_object      (GFileInfo  *info,
+                                                         const char *attribute);
+
+void               g_file_info_set_attribute             (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         const GFileAttributeValue *attr_value);
+void               g_file_info_set_attribute_string      (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         const char *attr_value);
+void               g_file_info_set_attribute_byte_string (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         const char *attr_value);
+void               g_file_info_set_attribute_boolean     (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         gboolean    attr_value);
+void               g_file_info_set_attribute_uint32      (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         guint32     attr_value);
+void               g_file_info_set_attribute_int32       (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         gint32      attr_value);
+void               g_file_info_set_attribute_uint64      (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         guint64     attr_value);
+void               g_file_info_set_attribute_int64       (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         gint64      attr_value);
+void               g_file_info_set_attribute_object      (GFileInfo  *info,
+                                                         const char *attribute,
+                                                         GObject    *attr_value);
+
+void               g_file_info_clear_status              (GFileInfo  *info);
+
+/* Helper getters: */
+GFileType         g_file_info_get_file_type          (GFileInfo         *info);
+gboolean          g_file_info_get_is_hidden          (GFileInfo         *info);
+gboolean          g_file_info_get_is_backup          (GFileInfo         *info);
+gboolean          g_file_info_get_is_symlink         (GFileInfo         *info);
+const char *      g_file_info_get_name               (GFileInfo         *info);
+const char *      g_file_info_get_display_name       (GFileInfo         *info);
+const char *      g_file_info_get_edit_name          (GFileInfo         *info);
+GIcon *           g_file_info_get_icon               (GFileInfo         *info);
+const char *      g_file_info_get_content_type       (GFileInfo         *info);
+goffset           g_file_info_get_size               (GFileInfo         *info);
+void              g_file_info_get_modification_time  (GFileInfo         *info,
+                                                     GTimeVal          *result);
+const char *      g_file_info_get_symlink_target     (GFileInfo         *info);
+const char *      g_file_info_get_etag               (GFileInfo         *info);
+gint32            g_file_info_get_sort_order         (GFileInfo         *info);
+
+void              g_file_info_set_attribute_mask     (GFileInfo         *info,
+                                                     GFileAttributeMatcher *mask);
+void              g_file_info_unset_attribute_mask   (GFileInfo         *info);
+
+/* Helper setters: */
+void              g_file_info_set_file_type          (GFileInfo         *info,
+                                                     GFileType          type);
+void              g_file_info_set_is_hidden          (GFileInfo         *info,
+                                                     gboolean           is_hidden);
+void              g_file_info_set_is_symlink         (GFileInfo         *info,
+                                                     gboolean           is_symlink);
+void              g_file_info_set_name               (GFileInfo         *info,
+                                                     const char        *name);
+void              g_file_info_set_display_name       (GFileInfo         *info,
+                                                     const char        *display_name);
+void              g_file_info_set_edit_name          (GFileInfo         *info,
+                                                     const char        *edit_name);
+void              g_file_info_set_icon               (GFileInfo         *info,
+                                                     GIcon             *icon);
+void              g_file_info_set_content_type       (GFileInfo         *info,
+                                                     const char        *content_type);
+void              g_file_info_set_size               (GFileInfo         *info,
+                                                     goffset            size);
+void              g_file_info_set_modification_time  (GFileInfo         *info,
+                                                     GTimeVal          *mtime);
+void              g_file_info_set_symlink_target     (GFileInfo         *info,
+                                                     const char        *symlink_target);
+void              g_file_info_set_sort_order         (GFileInfo         *info,
+                                                     gint32             sort_order);
+
+/* Helper functions for attributes: */
+/* TODO: Move this to glib when merging */
+char *g_format_file_size_for_display (goffset size);
+
+GFileAttributeMatcher *g_file_attribute_matcher_new            (const char            *attributes);
+GFileAttributeMatcher *g_file_attribute_matcher_ref            (GFileAttributeMatcher *matcher);
+void                   g_file_attribute_matcher_unref          (GFileAttributeMatcher *matcher);
+gboolean               g_file_attribute_matcher_matches        (GFileAttributeMatcher *matcher,
+                                                               const char            *attribute);
+gboolean               g_file_attribute_matcher_matches_only   (GFileAttributeMatcher *matcher,
+                                                               const char            *attribute);
+gboolean               g_file_attribute_matcher_enumerate_namespace (GFileAttributeMatcher *matcher,
+                                                                    const char            *ns);
+const char *           g_file_attribute_matcher_enumerate_next (GFileAttributeMatcher *matcher);
+
+G_END_DECLS
+
+
+#endif /* __G_FILE_INFO_H__ */
diff --git a/gio/gfileinputstream.c b/gio/gfileinputstream.c
new file mode 100644 (file)
index 0000000..24c8a61
--- /dev/null
@@ -0,0 +1,481 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <gfileinputstream.h>
+#include <gseekable.h>
+#include "gsimpleasyncresult.h"
+#include "glibintl.h"
+
+static void       g_file_input_stream_seekable_iface_init    (GSeekableIface       *iface);
+static goffset    g_file_input_stream_seekable_tell          (GSeekable            *seekable);
+static gboolean   g_file_input_stream_seekable_can_seek      (GSeekable            *seekable);
+static gboolean   g_file_input_stream_seekable_seek          (GSeekable            *seekable,
+                                                             goffset               offset,
+                                                             GSeekType             type,
+                                                             GCancellable         *cancellable,
+                                                             GError              **error);
+static gboolean   g_file_input_stream_seekable_can_truncate  (GSeekable            *seekable);
+static gboolean   g_file_input_stream_seekable_truncate      (GSeekable            *seekable,
+                                                             goffset               offset,
+                                                             GCancellable         *cancellable,
+                                                             GError              **error);
+static void       g_file_input_stream_real_query_info_async  (GFileInputStream     *stream,
+                                                             char                 *attributes,
+                                                             int                   io_priority,
+                                                             GCancellable         *cancellable,
+                                                             GAsyncReadyCallback   callback,
+                                                             gpointer              user_data);
+static GFileInfo *g_file_input_stream_real_query_info_finish (GFileInputStream     *stream,
+                                                             GAsyncResult         *result,
+                                                             GError              **error);
+
+
+G_DEFINE_TYPE_WITH_CODE (GFileInputStream, g_file_input_stream, G_TYPE_INPUT_STREAM,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+                                               g_file_input_stream_seekable_iface_init))
+
+struct _GFileInputStreamPrivate {
+  GAsyncReadyCallback outstanding_callback;
+};
+
+static void
+g_file_input_stream_class_init (GFileInputStreamClass *klass)
+{
+  g_type_class_add_private (klass, sizeof (GFileInputStreamPrivate));
+
+  klass->query_info_async = g_file_input_stream_real_query_info_async;
+  klass->query_info_finish = g_file_input_stream_real_query_info_finish;
+}
+
+static void
+g_file_input_stream_seekable_iface_init (GSeekableIface *iface)
+{
+  iface->tell = g_file_input_stream_seekable_tell;
+  iface->can_seek = g_file_input_stream_seekable_can_seek;
+  iface->seek = g_file_input_stream_seekable_seek;
+  iface->can_truncate = g_file_input_stream_seekable_can_truncate;
+  iface->truncate = g_file_input_stream_seekable_truncate;
+}
+
+static void
+g_file_input_stream_init (GFileInputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                             G_TYPE_FILE_INPUT_STREAM,
+                                             GFileInputStreamPrivate);
+}
+
+/**
+ * g_file_input_stream_query_info:
+ * @stream:
+ * @attributes:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+GFileInfo *
+g_file_input_stream_query_info (GFileInputStream     *stream,
+                                  char                 *attributes,
+                                  GCancellable         *cancellable,
+                                  GError              **error)
+{
+  GFileInputStreamClass *class;
+  GInputStream *input_stream;
+  GFileInfo *info;
+  
+  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
+  
+  input_stream = G_INPUT_STREAM (stream);
+  
+  if (g_input_stream_is_closed (input_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return NULL;
+    }
+  
+  if (g_input_stream_has_pending (input_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return NULL;
+    }
+      
+  info = NULL;
+  
+  g_input_stream_set_pending (input_stream, TRUE);
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
+  if (class->query_info)
+    info = class->query_info (stream, attributes, cancellable, error);
+  else
+    g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                _("Stream doesn't support query_info"));
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  g_input_stream_set_pending (input_stream, FALSE);
+  
+  return info;
+}
+
+static void
+async_ready_callback_wrapper (GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer      user_data)
+{
+  GFileInputStream *stream = G_FILE_INPUT_STREAM (source_object);
+
+  g_input_stream_set_pending (G_INPUT_STREAM (stream), FALSE);
+  if (stream->priv->outstanding_callback)
+    (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+/**
+ * g_file_input_stream_query_info_async:
+ * @stream:
+ * @attributes:
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore. @callback:
+ * @user_data:
+ *  
+ **/
+void
+g_file_input_stream_query_info_async (GFileInputStream     *stream,
+                                        char                 *attributes,
+                                        int                   io_priority,
+                                        GCancellable         *cancellable,
+                                        GAsyncReadyCallback   callback,
+                                        gpointer              user_data)
+{
+  GFileInputStreamClass *klass;
+  GInputStream *input_stream;
+
+  g_return_if_fail (G_IS_FILE_INPUT_STREAM (stream));
+
+  input_stream = G_INPUT_STREAM (stream);
+  if (g_input_stream_is_closed (input_stream))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+  
+  if (g_input_stream_has_pending (input_stream))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  klass = G_FILE_INPUT_STREAM_GET_CLASS (stream);
+
+  g_input_stream_set_pending (input_stream, TRUE);
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  klass->query_info_async (stream, attributes, io_priority, cancellable,
+                             async_ready_callback_wrapper, user_data);
+}
+
+/**
+ * g_file_input_stream_query_info_finish:
+ * @stream:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: #GFileInfo. 
+ **/
+GFileInfo *
+g_file_input_stream_query_info_finish (GFileInputStream     *stream,
+                                         GAsyncResult         *result,
+                                         GError              **error)
+{
+  GSimpleAsyncResult *simple;
+  GFileInputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+
+  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
+  return class->query_info_finish (stream, result, error);
+}
+
+/**
+ * g_file_input_stream_tell:
+ * @stream:
+ * 
+ * Returns: 
+ **/
+goffset
+g_file_input_stream_tell (GFileInputStream  *stream)
+{
+  GFileInputStreamClass *class;
+  goffset offset;
+
+  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), 0);
+
+  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
+
+  offset = 0;
+  if (class->tell)
+    offset = class->tell (stream);
+
+  return offset;
+}
+
+static goffset
+g_file_input_stream_seekable_tell (GSeekable *seekable)
+{
+  return g_file_input_stream_tell (G_FILE_INPUT_STREAM (seekable));
+}
+
+/**
+ * g_file_input_stream_can_seek:
+ * @stream:
+ * 
+ * Returns: %TRUE if stream can be seeked. %FALSE otherwise.
+ **/
+gboolean
+g_file_input_stream_can_seek (GFileInputStream  *stream)
+{
+  GFileInputStreamClass *class;
+  gboolean can_seek;
+
+  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
+
+  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
+
+  can_seek = FALSE;
+  if (class->seek)
+    {
+      can_seek = TRUE;
+      if (class->can_seek)
+       can_seek = class->can_seek (stream);
+    }
+  
+  return can_seek;
+}
+
+static gboolean
+g_file_input_stream_seekable_can_seek (GSeekable *seekable)
+{
+  return g_file_input_stream_can_seek (G_FILE_INPUT_STREAM (seekable));
+}
+
+/**
+ * g_file_input_stream_seek:
+ * @stream:
+ * @offset:
+ * @type:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+gboolean
+g_file_input_stream_seek (GFileInputStream  *stream,
+                         goffset            offset,
+                         GSeekType          type,
+                         GCancellable      *cancellable,
+                         GError           **error)
+{
+  GFileInputStreamClass *class;
+  GInputStream *input_stream;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_FILE_INPUT_STREAM (stream), FALSE);
+
+  input_stream = G_INPUT_STREAM (stream);
+  class = G_FILE_INPUT_STREAM_GET_CLASS (stream);
+
+  if (g_input_stream_is_closed (input_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return FALSE;
+    }
+  
+  if (g_input_stream_has_pending (input_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return FALSE;
+    }
+  
+  if (!class->seek)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Seek not supported on stream"));
+      return FALSE;
+    }
+
+  g_input_stream_set_pending (input_stream, TRUE);
+  
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  res = class->seek (stream, offset, type, cancellable, error);
+  
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+
+  g_input_stream_set_pending (input_stream, FALSE);
+  
+  return res;
+}
+
+static gboolean
+g_file_input_stream_seekable_seek (GSeekable  *seekable,
+                                  goffset     offset,
+                                  GSeekType   type,
+                                  GCancellable  *cancellable,
+                                  GError    **error)
+{
+  return g_file_input_stream_seek (G_FILE_INPUT_STREAM (seekable),
+                                  offset, type, cancellable, error);
+}
+
+static gboolean
+g_file_input_stream_seekable_can_truncate (GSeekable  *seekable)
+{
+  return FALSE;
+}
+
+static gboolean
+g_file_input_stream_seekable_truncate (GSeekable  *seekable,
+                                      goffset     offset,
+                                      GCancellable  *cancellable,
+                                      GError    **error)
+{
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+              _("Truncate not allowed on input stream"));
+  return FALSE;
+}
+
+/********************************************
+ *   Default implementation of async ops    *
+ ********************************************/
+
+typedef struct {
+  char *attributes;
+  GFileInfo *info;
+} QueryInfoAsyncData;
+
+static void
+query_info_data_free (QueryInfoAsyncData *data)
+{
+  if (data->info)
+    g_object_unref (data->info);
+  g_free (data->attributes);
+  g_free (data);
+}
+
+static void
+query_info_async_thread (GSimpleAsyncResult *res,
+                      GObject *object,
+                      GCancellable *cancellable)
+{
+  GFileInputStreamClass *class;
+  GError *error = NULL;
+  QueryInfoAsyncData *data;
+  GFileInfo *info;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  info = NULL;
+  
+  class = G_FILE_INPUT_STREAM_GET_CLASS (object);
+  if (class->query_info)
+    info = class->query_info (G_FILE_INPUT_STREAM (object), data->attributes, cancellable, &error);
+  else
+    g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                _("Stream doesn't support query_info"));
+
+  if (info == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->info = info;
+}
+
+static void
+g_file_input_stream_real_query_info_async (GFileInputStream     *stream,
+                                             char                 *attributes,
+                                             int                   io_priority,
+                                             GCancellable         *cancellable,
+                                             GAsyncReadyCallback   callback,
+                                             gpointer              user_data)
+{
+  GSimpleAsyncResult *res;
+  QueryInfoAsyncData *data;
+
+  data = g_new0 (QueryInfoAsyncData, 1);
+  data->attributes = g_strdup (attributes);
+  
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_file_input_stream_real_query_info_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free);
+  
+  g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileInfo *
+g_file_input_stream_real_query_info_finish (GFileInputStream     *stream,
+                                              GAsyncResult         *res,
+                                              GError              **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  QueryInfoAsyncData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_input_stream_real_query_info_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->info)
+    return g_object_ref (data->info);
+  
+  return NULL;
+}
diff --git a/gio/gfileinputstream.h b/gio/gfileinputstream.h
new file mode 100644 (file)
index 0000000..ecf8766
--- /dev/null
@@ -0,0 +1,110 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_INPUT_STREAM_H__
+#define __G_FILE_INPUT_STREAM_H__
+
+#include <gio/ginputstream.h>
+#include <gio/gfileinfo.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILE_INPUT_STREAM         (g_file_input_stream_get_type ())
+#define G_FILE_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILE_INPUT_STREAM, GFileInputStream))
+#define G_FILE_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILE_INPUT_STREAM, GFileInputStreamClass))
+#define G_IS_FILE_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILE_INPUT_STREAM))
+#define G_IS_FILE_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILE_INPUT_STREAM))
+#define G_FILE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILE_INPUT_STREAM, GFileInputStreamClass))
+
+typedef struct _GFileInputStream         GFileInputStream;
+typedef struct _GFileInputStreamClass    GFileInputStreamClass;
+typedef struct _GFileInputStreamPrivate  GFileInputStreamPrivate;
+
+struct _GFileInputStream
+{
+  GInputStream parent;
+
+  /*< private >*/
+  GFileInputStreamPrivate *priv;
+};
+
+struct _GFileInputStreamClass
+{
+  GInputStreamClass parent_class;
+
+  goffset    (*tell)          (GFileInputStream     *stream);
+  gboolean   (*can_seek)      (GFileInputStream     *stream);
+  gboolean   (*seek)         (GFileInputStream     *stream,
+                              goffset               offset,
+                              GSeekType             type,
+                              GCancellable         *cancellable,
+                              GError              **error);
+  GFileInfo *(*query_info)    (GFileInputStream     *stream,
+                              char                 *attributes,
+                              GCancellable         *cancellable,
+                              GError              **error);
+  void       (*query_info_async)  (GFileInputStream     *stream,
+                                  char                 *attributes,
+                                  int                   io_priority,
+                                  GCancellable         *cancellable,
+                                  GAsyncReadyCallback   callback,
+                                  gpointer              user_data);
+  GFileInfo *(*query_info_finish) (GFileInputStream     *stream,
+                                  GAsyncResult         *res,
+                                  GError              **error);
+    
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_file_input_stream_get_type (void) G_GNUC_CONST;
+
+GFileInfo *g_file_input_stream_query_info        (GFileInputStream     *stream,
+                                                 char                 *attributes,
+                                                 GCancellable         *cancellable,
+                                                 GError              **error);
+void       g_file_input_stream_query_info_async  (GFileInputStream     *stream,
+                                                 char                 *attributes,
+                                                 int                   io_priority,
+                                                 GCancellable         *cancellable,
+                                                 GAsyncReadyCallback   callback,
+                                                 gpointer              user_data);
+GFileInfo *g_file_input_stream_query_info_finish (GFileInputStream     *stream,
+                                                 GAsyncResult         *result,
+                                                 GError              **error);
+goffset    g_file_input_stream_tell              (GFileInputStream     *stream);
+gboolean   g_file_input_stream_can_seek          (GFileInputStream     *stream);
+gboolean   g_file_input_stream_seek              (GFileInputStream     *stream,
+                                                 goffset               offset,
+                                                 GSeekType             type,
+                                                 GCancellable         *cancellable,
+                                                 GError              **error);
+
+
+
+G_END_DECLS
+
+#endif /* __G_FILE_FILE_INPUT_STREAM_H__ */
diff --git a/gio/gfilemonitor.c b/gio/gfilemonitor.c
new file mode 100644 (file)
index 0000000..ba9fa56
--- /dev/null
@@ -0,0 +1,380 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "gfilemonitor.h"
+#include "gio-marshal.h"
+#include "gvfs.h"
+#include "glibintl.h"
+
+enum {
+  CHANGED,
+  LAST_SIGNAL
+};
+
+G_DEFINE_ABSTRACT_TYPE (GFileMonitor, g_file_monitor, G_TYPE_OBJECT);
+
+struct _GFileMonitorPrivate {
+  gboolean cancelled;
+  int rate_limit_msec;
+
+  /* Rate limiting change events */
+  guint32 last_sent_change_time; /* Some monitonic clock in msecs */
+  GFile *last_sent_change_file;
+  
+  guint send_delayed_change_timeout;
+
+  /* Virtual CHANGES_DONE_HINT emission */
+  GSource *virtual_changes_done_timeout;
+  GFile *virtual_changes_done_file;
+};
+
+#define DEFAULT_RATE_LIMIT_MSECS 800
+#define DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS 2
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static void
+g_file_monitor_finalize (GObject *object)
+{
+  GFileMonitor *monitor;
+
+  monitor = G_FILE_MONITOR (object);
+
+  if (monitor->priv->last_sent_change_file)
+    g_object_unref (monitor->priv->last_sent_change_file);
+
+  if (monitor->priv->send_delayed_change_timeout != 0)
+    g_source_remove (monitor->priv->send_delayed_change_timeout);
+
+  if (monitor->priv->virtual_changes_done_file)
+    g_object_unref (monitor->priv->virtual_changes_done_file);
+
+  if (monitor->priv->virtual_changes_done_timeout)
+    g_source_destroy (monitor->priv->virtual_changes_done_timeout);
+  
+  if (G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize) (object);
+}
+
+static void
+g_file_monitor_dispose (GObject *object)
+{
+  GFileMonitor *monitor;
+  
+  monitor = G_FILE_MONITOR (object);
+
+  /* Make sure we cancel on last unref */
+  if (!monitor->priv->cancelled)
+    g_file_monitor_cancel (monitor);
+  
+  if (G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose)
+    (*G_OBJECT_CLASS (g_file_monitor_parent_class)->dispose) (object);
+}
+
+static void
+g_file_monitor_class_init (GFileMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GFileMonitorPrivate));
+  
+  gobject_class->finalize = g_file_monitor_finalize;
+  gobject_class->dispose = g_file_monitor_dispose;
+
+  signals[CHANGED] =
+    g_signal_new (I_("changed"),
+                 G_TYPE_FILE_MONITOR,
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GFileMonitorClass, changed),
+                 NULL, NULL,
+                 _gio_marshal_VOID__OBJECT_OBJECT_INT,
+                 G_TYPE_NONE,3,
+                 G_TYPE_FILE,
+                 G_TYPE_FILE,
+                 G_TYPE_INT);
+}
+
+static void
+g_file_monitor_init (GFileMonitor *monitor)
+{
+  monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
+                                              G_TYPE_FILE_MONITOR,
+                                              GFileMonitorPrivate);
+  monitor->priv->rate_limit_msec = DEFAULT_RATE_LIMIT_MSECS;
+}
+
+/**
+ * g_file_monitor_is_cancelled:
+ * @monitor:
+ * 
+ * Returns: %TRUE if monitor is canceled. %FALSE otherwise.
+ **/
+gboolean
+g_file_monitor_is_cancelled (GFileMonitor *monitor)
+{
+  g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
+
+  return monitor->priv->cancelled;
+}
+
+/**
+ * g_file_monitor_cancel:
+ * @monitor:
+ * 
+ * Returns: %TRUE if monitor was cancelled.
+ **/
+gboolean
+g_file_monitor_cancel (GFileMonitor* monitor)
+{
+  GFileMonitorClass *klass;
+  
+  g_return_val_if_fail (G_IS_FILE_MONITOR (monitor), FALSE);
+  
+  if (monitor->priv->cancelled)
+    return TRUE;
+  
+  monitor->priv->cancelled = TRUE;
+  
+  klass = G_FILE_MONITOR_GET_CLASS (monitor);
+  return (* klass->cancel) (monitor);
+}
+
+/**
+ * g_file_monitor_set_rate_limit:
+ * @monitor: a #GFileMonitor.
+ * @limit_msecs: a integer with the limit in milliseconds to 
+ * poll for changes.
+ *
+ * Sets the rate limit to which the @monitor will poll for changes 
+ * on the device. 
+ * 
+ **/
+void
+g_file_monitor_set_rate_limit (GFileMonitor *monitor,
+                              int           limit_msecs)
+{
+  g_return_if_fail (G_IS_FILE_MONITOR (monitor));
+
+  monitor->priv->rate_limit_msec = limit_msecs;
+}
+
+static guint32
+get_time_msecs (void)
+{
+  return g_thread_gettime() / (1000 * 1000);
+}
+
+static guint32
+time_difference (guint32 from, guint32 to)
+{
+  if (from > to)
+    return 0;
+  return to - from;
+}
+
+/* Change event rate limiting support: */
+
+static void
+update_last_sent_change (GFileMonitor *monitor, GFile *file, guint32 time_now)
+{
+  if (monitor->priv->last_sent_change_file != file)
+    {
+      if (monitor->priv->last_sent_change_file)
+       {
+         g_object_unref (monitor->priv->last_sent_change_file);
+         monitor->priv->last_sent_change_file = NULL;
+       }
+      if (file)
+       monitor->priv->last_sent_change_file = g_object_ref (file);
+    }
+  
+  monitor->priv->last_sent_change_time = time_now;
+}
+
+static void
+send_delayed_change_now (GFileMonitor *monitor)
+{
+  if (monitor->priv->send_delayed_change_timeout)
+    {
+      g_signal_emit (monitor, signals[CHANGED], 0,
+                    monitor->priv->last_sent_change_file, NULL,
+                    G_FILE_MONITOR_EVENT_CHANGED);
+      
+      g_source_remove (monitor->priv->send_delayed_change_timeout);
+      monitor->priv->send_delayed_change_timeout = 0;
+
+      /* Same file, new last_sent time */
+      monitor->priv->last_sent_change_time = get_time_msecs ();
+    }
+}
+
+static gboolean
+delayed_changed_event_timeout (gpointer data)
+{
+  GFileMonitor *monitor = data;
+
+  send_delayed_change_now (monitor);
+  
+  return FALSE;
+}
+
+static void
+schedule_delayed_change (GFileMonitor *monitor, GFile *file, guint32 delay_msec)
+{
+  if (monitor->priv->send_delayed_change_timeout == 0) /* Only set the timeout once */
+    {
+      monitor->priv->send_delayed_change_timeout = 
+       g_timeout_add (delay_msec, delayed_changed_event_timeout, monitor);
+    }
+}
+
+static void
+cancel_delayed_change (GFileMonitor *monitor)
+{
+  if (monitor->priv->send_delayed_change_timeout != 0)
+    {
+      g_source_remove (monitor->priv->send_delayed_change_timeout);
+      monitor->priv->send_delayed_change_timeout = 0;
+    }
+}
+
+/* Virtual changes_done_hint support: */
+
+static void
+send_virtual_changes_done_now (GFileMonitor *monitor)
+{
+  if (monitor->priv->virtual_changes_done_timeout)
+    {
+      g_signal_emit (monitor, signals[CHANGED], 0,
+                    monitor->priv->virtual_changes_done_file, NULL,
+                    G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
+      
+      g_source_destroy (monitor->priv->virtual_changes_done_timeout);
+      monitor->priv->virtual_changes_done_timeout = NULL;
+
+      g_object_unref (monitor->priv->virtual_changes_done_file);
+      monitor->priv->virtual_changes_done_file = NULL;
+    }
+}
+
+static gboolean
+virtual_changes_done_timeout (gpointer data)
+{
+  GFileMonitor *monitor = data;
+
+  send_virtual_changes_done_now (monitor);
+  
+  return FALSE;
+}
+
+static void
+schedule_virtual_change_done (GFileMonitor *monitor, GFile *file)
+{
+  GSource *source;
+  
+  source = g_timeout_source_new_seconds (DEFAULT_VIRTUAL_CHANGES_DONE_DELAY_SECS);
+  
+  g_source_set_callback (source, virtual_changes_done_timeout, monitor, NULL);
+  g_source_attach (source, NULL);
+  monitor->priv->virtual_changes_done_timeout = source;
+  monitor->priv->virtual_changes_done_file = g_object_ref (file);
+  g_source_unref (source);
+}
+
+static void
+cancel_virtual_changes_done (GFileMonitor *monitor)
+{
+  if (monitor->priv->virtual_changes_done_timeout)
+    {
+      g_source_destroy (monitor->priv->virtual_changes_done_timeout);
+      monitor->priv->virtual_changes_done_timeout = NULL;
+      
+      g_object_unref (monitor->priv->virtual_changes_done_file);
+      monitor->priv->virtual_changes_done_file = NULL;
+    }
+}
+
+/**
+ * g_file_monitor_emit_event:
+ * @monitor:
+ * @file:
+ * @other_file:
+ * @event_type:
+ * 
+ **/
+void
+g_file_monitor_emit_event (GFileMonitor *monitor,
+                          GFile *file,
+                          GFile *other_file,
+                          GFileMonitorEvent event_type)
+{
+  guint32 time_now, since_last;
+  gboolean emit_now;
+
+  g_return_if_fail (G_IS_FILE_MONITOR (monitor));
+  g_return_if_fail (G_IS_FILE (file));
+
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGED)
+    {
+      send_delayed_change_now (monitor);
+      update_last_sent_change (monitor, NULL, 0);
+      if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+       cancel_virtual_changes_done (monitor);
+      else
+       send_virtual_changes_done_now (monitor);
+      g_signal_emit (monitor, signals[CHANGED], 0, file, other_file, event_type);
+    }
+  else
+    {
+      time_now = get_time_msecs ();
+      emit_now = TRUE;
+      
+      if (monitor->priv->last_sent_change_file)
+       {
+         since_last = time_difference (monitor->priv->last_sent_change_time, time_now);
+         if (since_last < monitor->priv->rate_limit_msec)
+           {
+             /* We ignore this change, but arm a timer so that we can fire it later if we
+                don't get any other events (that kill this timeout) */
+             emit_now = FALSE;
+             schedule_delayed_change (monitor, file,
+                                      monitor->priv->rate_limit_msec - since_last);
+           }
+       }
+      
+      if (emit_now)
+       {
+         g_signal_emit (monitor, signals[CHANGED], 0, file, other_file, event_type);
+         
+         cancel_delayed_change (monitor);
+         update_last_sent_change (monitor, file, time_now);
+       }
+
+      /* Schedule a virtual change done. This is removed if we get a real one, and
+        postponed if we get more change events. */
+      cancel_virtual_changes_done (monitor);
+      schedule_virtual_change_done (monitor, file);
+    }
+}
diff --git a/gio/gfilemonitor.h b/gio/gfilemonitor.h
new file mode 100644 (file)
index 0000000..edb9f64
--- /dev/null
@@ -0,0 +1,97 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_MONITOR_H__
+#define __G_FILE_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILE_MONITOR         (g_file_monitor_get_type ())
+#define G_FILE_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILE_MONITOR, GFileMonitor))
+#define G_FILE_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILE_MONITOR, GFileMonitorClass))
+#define G_IS_FILE_MONITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILE_MONITOR))
+#define G_IS_FILE_MONITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILE_MONITOR))
+#define G_FILE_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILE_MONITOR, GFileMonitorClass))
+
+typedef enum {
+  G_FILE_MONITOR_EVENT_CHANGED,
+  G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT,
+  G_FILE_MONITOR_EVENT_DELETED,
+  G_FILE_MONITOR_EVENT_CREATED,
+  G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED,
+  G_FILE_MONITOR_EVENT_PRE_UNMOUNT,
+  G_FILE_MONITOR_EVENT_UNMOUNTED
+} GFileMonitorEvent;
+
+typedef struct _GFileMonitorClass      GFileMonitorClass;
+typedef struct _GFileMonitorPrivate    GFileMonitorPrivate;
+
+struct _GFileMonitor
+{
+  GObject parent;
+
+  /*< private >*/
+  GFileMonitorPrivate *priv;
+};
+
+struct _GFileMonitorClass
+{
+  GObjectClass parent_class;
+  
+  /* Signals */
+  void (* changed) (GFileMonitor* monitor,
+                   GFile* file,
+                   GFile* other_file,
+                   GFileMonitorEvent event_type);
+  
+  /* Virtual Table */
+  gboolean     (*cancel)(GFileMonitor* monitor);
+
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_file_monitor_get_type (void) G_GNUC_CONST;
+
+gboolean g_file_monitor_cancel         (GFileMonitor *monitor);
+gboolean g_file_monitor_is_cancelled   (GFileMonitor *monitor);
+void     g_file_monitor_set_rate_limit (GFileMonitor *monitor,
+                                       int           limit_msecs);
+
+
+/* For implementations */
+void g_file_monitor_emit_event (GFileMonitor      *monitor,
+                               GFile             *file,
+                               GFile             *other_file,
+                               GFileMonitorEvent  event_type);
+
+G_END_DECLS
+
+#endif /* __G_FILE_MONITOR_H__ */
diff --git a/gio/gfilenamecompleter.c b/gio/gfilenamecompleter.c
new file mode 100644 (file)
index 0000000..c8d85d8
--- /dev/null
@@ -0,0 +1,489 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gfilenamecompleter.h"
+#include "gurifuncs.h"
+#include "gfile.h"
+#include <string.h>
+#include "glibintl.h"
+
+enum {
+  GOT_COMPLETION_DATA,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct {
+  GFilenameCompleter *completer;
+  GFileEnumerator *enumerator;
+  GCancellable *cancellable;
+  gboolean should_escape;
+  GFile *dir;
+  GList *basenames;
+  gboolean dirs_only;
+} LoadBasenamesData;
+
+struct _GFilenameCompleter {
+  GObject parent;
+
+  GFile *basenames_dir;
+  gboolean basenames_are_escaped;
+  GList *basenames;
+  gboolean dirs_only;
+
+  LoadBasenamesData *basename_loader;
+};
+
+G_DEFINE_TYPE (GFilenameCompleter, g_filename_completer, G_TYPE_OBJECT);
+
+static void cancel_load_basenames (GFilenameCompleter *completer);
+
+static void
+g_filename_completer_finalize (GObject *object)
+{
+  GFilenameCompleter *completer;
+
+  completer = G_FILENAME_COMPLETER (object);
+
+  cancel_load_basenames (completer);
+
+  if (completer->basenames_dir)
+    g_object_unref (completer->basenames_dir);
+
+  g_list_foreach (completer->basenames, (GFunc)g_free, NULL);
+  g_list_free (completer->basenames);
+  
+  if (G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_filename_completer_parent_class)->finalize) (object);
+}
+
+static void
+g_filename_completer_class_init (GFilenameCompleterClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_filename_completer_finalize;
+
+  signals[GOT_COMPLETION_DATA] = g_signal_new (I_("got_completion_data"),
+                                         G_TYPE_FILENAME_COMPLETER,
+                                         G_SIGNAL_RUN_LAST,
+                                         G_STRUCT_OFFSET (GFilenameCompleterClass, got_completion_data),
+                                         NULL, NULL,
+                                         g_cclosure_marshal_VOID__VOID,
+                                         G_TYPE_NONE, 0);
+}
+
+static void
+g_filename_completer_init (GFilenameCompleter *completer)
+{
+}
+
+/**
+ * g_filename_completer_new:
+ * 
+ * Returns: a new #GFilenameCompleter.
+ **/
+GFilenameCompleter *
+g_filename_completer_new (void)
+{
+  return g_object_new (G_TYPE_FILENAME_COMPLETER, NULL);
+}
+
+static char *
+longest_common_prefix (char *a, char *b)
+{
+  char *start;
+
+  start = a;
+
+  while (g_utf8_get_char (a) == g_utf8_get_char (b))
+    {
+      a = g_utf8_next_char (a);
+      b = g_utf8_next_char (b);
+    }
+
+  return g_strndup (start, a - start);
+}
+
+static void
+load_basenames_data_free (LoadBasenamesData *data)
+{
+  if (data->enumerator)
+    g_object_unref (data->enumerator);
+  
+  g_object_unref (data->cancellable);
+  g_object_unref (data->dir);
+  
+  g_list_foreach (data->basenames, (GFunc)g_free, NULL);
+  g_list_free (data->basenames);
+  
+  g_free (data);
+}
+
+static void
+got_more_files (GObject *source_object,
+               GAsyncResult *res,
+               gpointer user_data)
+{
+  LoadBasenamesData *data = user_data;
+  GList *infos, *l;
+  GFileInfo *info;
+  const char *name;
+  gboolean append_slash;
+  char *t;
+  char *basename;
+
+  if (data->completer == NULL)
+    {
+      /* Was cancelled */
+      load_basenames_data_free (data);
+      return;
+    }
+
+  infos = g_file_enumerator_next_files_finish (data->enumerator, res, NULL);
+
+  for (l = infos; l != NULL; l = l->next)
+    {
+      info = l->data;
+
+      if (data->dirs_only &&
+         g_file_info_get_file_type (info) != G_FILE_TYPE_DIRECTORY)
+       {
+         g_object_unref (info);
+         continue;
+       }
+      
+      append_slash = g_file_info_get_file_type (info) == G_FILE_TYPE_DIRECTORY;
+      name = g_file_info_get_name (info);
+      if (name == NULL)
+       {
+         g_object_unref (info);
+         continue;
+       }
+
+      
+      if (data->should_escape)
+       basename = g_uri_escape_string (name,
+                                       G_URI_RESERVED_CHARS_ALLOWED_IN_PATH,
+                                       TRUE);
+      else
+       /* If not should_escape, must be a local filename, convert to utf8 */
+       basename = g_filename_to_utf8 (name, -1, NULL, NULL, NULL);
+      
+      if (basename)
+       {
+         if (append_slash)
+           {
+             t = basename;
+             basename = g_strconcat (basename, "/", NULL);
+             g_free (t);
+           }
+         
+         data->basenames = g_list_prepend (data->basenames, basename);
+       }
+      
+      g_object_unref (info);
+    }
+  
+  g_list_free (infos);
+  
+  if (infos)
+    {
+      /* Not last, get more files */
+      g_file_enumerator_next_files_async (data->enumerator,
+                                         100,
+                                         0,
+                                         data->cancellable,
+                                         got_more_files, data);
+    }
+  else
+    {
+      data->completer->basename_loader = NULL;
+      
+      if (data->completer->basenames_dir)
+       g_object_unref (data->completer->basenames_dir);
+      g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
+      g_list_free (data->completer->basenames);
+      
+      data->completer->basenames_dir = g_object_ref (data->dir);
+      data->completer->basenames = data->basenames;
+      data->completer->basenames_are_escaped = data->should_escape;
+      data->basenames = NULL;
+      
+      g_file_enumerator_close_async (data->enumerator, 0, NULL, NULL, NULL);
+
+      g_signal_emit (data->completer, signals[GOT_COMPLETION_DATA], 0);
+      load_basenames_data_free (data);
+    }
+}
+
+
+static void
+got_enum (GObject *source_object,
+         GAsyncResult *res,
+         gpointer user_data)
+{
+  LoadBasenamesData *data = user_data;
+
+  if (data->completer == NULL)
+    {
+      /* Was cancelled */
+      load_basenames_data_free (data);
+      return;
+    }
+  
+  data->enumerator = g_file_enumerate_children_finish (G_FILE (source_object), res, NULL);
+  
+  if (data->enumerator == NULL)
+    {
+      data->completer->basename_loader = NULL;
+
+      if (data->completer->basenames_dir)
+       g_object_unref (data->completer->basenames_dir);
+      g_list_foreach (data->completer->basenames, (GFunc)g_free, NULL);
+      g_list_free (data->completer->basenames);
+
+      /* Mark uptodate with no basenames */
+      data->completer->basenames_dir = g_object_ref (data->dir);
+      data->completer->basenames = NULL;
+      data->completer->basenames_are_escaped = data->should_escape;
+      
+      load_basenames_data_free (data);
+      return;
+    }
+  
+  g_file_enumerator_next_files_async (data->enumerator,
+                                     100,
+                                     0,
+                                     data->cancellable,
+                                     got_more_files, data);
+}
+
+static void
+schedule_load_basenames (GFilenameCompleter *completer,
+                        GFile *dir,
+                        gboolean should_escape)
+{
+  LoadBasenamesData *data;
+
+  cancel_load_basenames (completer);
+
+  data = g_new0 (LoadBasenamesData, 1);
+  data->completer = completer;
+  data->cancellable = g_cancellable_new ();
+  data->dir = g_object_ref (dir);
+  data->should_escape = should_escape;
+  data->dirs_only = completer->dirs_only;
+
+  completer->basename_loader = data;
+  
+  g_file_enumerate_children_async (dir,
+                                  G_FILE_ATTRIBUTE_STD_NAME "," G_FILE_ATTRIBUTE_STD_TYPE,
+                                  0, 0,
+                                  data->cancellable,
+                                  got_enum, data);
+}
+
+static void
+cancel_load_basenames (GFilenameCompleter *completer)
+{
+  LoadBasenamesData *loader;
+  
+  if (completer->basename_loader)
+    {
+      loader = completer->basename_loader; 
+      loader->completer = NULL;
+      
+      g_cancellable_cancel (loader->cancellable);
+      
+      completer->basename_loader = NULL;
+    }
+}
+
+
+/* Returns a list of possible matches and the basename to use for it */
+static GList *
+init_completion (GFilenameCompleter *completer,
+                const char *initial_text,
+                char **basename_out)
+{
+  gboolean should_escape;
+  GFile *file, *parent;
+  char *basename;
+  char *t;
+  int len;
+
+  *basename_out = NULL;
+  
+  should_escape = ! (g_path_is_absolute (initial_text) || *initial_text == '~');
+
+  len = strlen (initial_text);
+  
+  if (len > 0 &&
+      initial_text[len - 1] == '/')
+    return NULL;
+  
+  file = g_file_parse_name (initial_text);
+  parent = g_file_get_parent (file);
+  if (parent == NULL)
+    {
+      g_object_unref (file);
+      return NULL;
+    }
+
+  if (completer->basenames_dir == NULL ||
+      completer->basenames_are_escaped != should_escape ||
+      !g_file_equal (parent, completer->basenames_dir))
+    {
+      schedule_load_basenames (completer, parent, should_escape);
+      g_object_unref (file);
+      return NULL;
+    }
+  
+  basename = g_file_get_basename (file);
+  if (should_escape)
+    {
+      t = basename;
+      basename = g_uri_escape_string (basename, G_URI_RESERVED_CHARS_ALLOWED_IN_PATH, TRUE);
+      g_free (t);
+    }
+  else
+    {
+      t = basename;
+      basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
+      g_free (t);
+      
+      if (basename == NULL)
+       return NULL;
+    }
+
+  *basename_out = basename;
+
+  return completer->basenames;
+}
+
+/**
+ * g_filename_completer_get_completion_suffix:
+ * @completer: the filename completer.
+ * @initial_text: text to be completed.
+ * 
+ * Returns: a completed string. This string is not owned by GIO, so
+ * remember to g_free() it when finished.
+ **/
+char *
+g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
+                                           const char *initial_text)
+{
+  GList *possible_matches, *l;
+  char *prefix;
+  char *suffix;
+  char *possible_match;
+  char *lcp;
+
+  g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
+  g_return_val_if_fail (initial_text != NULL, NULL);
+
+  possible_matches = init_completion (completer, initial_text, &prefix);
+
+  suffix = NULL;
+  
+  for (l = possible_matches; l != NULL; l = l->next)
+    {
+      possible_match = l->data;
+      
+      if (g_str_has_prefix (possible_match, prefix))
+       {
+         if (suffix == NULL)
+           suffix = g_strdup (possible_match + strlen (prefix));
+         else
+           {
+             lcp = longest_common_prefix (suffix,
+                                          possible_match + strlen (prefix));
+             g_free (suffix);
+             suffix = lcp;
+             
+             if (*suffix == 0)
+               break;
+           }
+       }
+    }
+
+  g_free (prefix);
+  
+  return suffix;
+}
+
+/**
+ * g_filename_completer_get_completions:
+ * @completer: the filename completer.
+ * @initial_text: text to be completed.
+ * 
+ * Returns: array of strings with possible completions for @initial_text.
+ * This array must be freed by g_strfreev() when finished. 
+ **/
+char **
+g_filename_completer_get_completions (GFilenameCompleter *completer,
+                                     const char *initial_text)
+{
+  GList *possible_matches, *l;
+  char *prefix;
+  char *possible_match;
+  GPtrArray *res;
+
+  g_return_val_if_fail (G_IS_FILENAME_COMPLETER (completer), NULL);
+  g_return_val_if_fail (initial_text != NULL, NULL);
+
+  possible_matches = init_completion (completer, initial_text, &prefix);
+
+  res = g_ptr_array_new ();
+  for (l = possible_matches; l != NULL; l = l->next)
+    {
+      possible_match = l->data;
+      
+      if (g_str_has_prefix (possible_match, prefix))
+       g_ptr_array_add (res,
+                        g_strconcat (initial_text, possible_match + strlen (prefix), NULL));
+    }
+
+  g_free (prefix);
+  
+  return (char**)g_ptr_array_free (res, FALSE);
+}
+
+/**
+ * g_filename_completer_set_dirs_only:
+ * @completer: the filename completer.
+ * @dirs_only: 
+ * 
+ * If @dirs_only is %TRUE, @completer will only 
+ * complete directory names, and not file names.
+ **/
+void
+g_filename_completer_set_dirs_only (GFilenameCompleter *completer,
+                                   gboolean dirs_only)
+{
+  g_return_if_fail (G_IS_FILENAME_COMPLETER (completer));
+
+  completer->dirs_only = dirs_only;
+}
diff --git a/gio/gfilenamecompleter.h b/gio/gfilenamecompleter.h
new file mode 100644 (file)
index 0000000..cf4cae1
--- /dev/null
@@ -0,0 +1,66 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILENAME_COMPLETER_H__
+#define __G_FILENAME_COMPLETER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILENAME_COMPLETER         (g_filename_completer_get_type ())
+#define G_FILENAME_COMPLETER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILENAME_COMPLETER, GFilenameCompleter))
+#define G_FILENAME_COMPLETER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILENAME_COMPLETER, GFilenameCompleterClass))
+#define G_FILENAME_COMPLETER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILENAME_COMPLETER, GFilenameCompleterClass))
+#define G_IS_FILENAME_COMPLETER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILENAME_COMPLETER))
+#define G_IS_FILENAME_COMPLETER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILENAME_COMPLETER))
+
+typedef struct _GFilenameCompleter GFilenameCompleter;
+typedef struct _GFilenameCompleterClass GFilenameCompleterClass;
+
+struct _GFilenameCompleterClass {
+  GObjectClass parent_class;
+
+  /*< public >*/
+  /* signals */
+  void (* got_completion_data) (GFilenameCompleter *filename_completer);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+};
+
+GType g_filename_completer_get_type (void) G_GNUC_CONST;
+
+GFilenameCompleter *g_filename_completer_new                   (void);
+
+char *              g_filename_completer_get_completion_suffix (GFilenameCompleter *completer,
+                                                               const char *initial_text);
+char **             g_filename_completer_get_completions       (GFilenameCompleter *completer,
+                                                               const char *initial_text);
+void                g_filename_completer_set_dirs_only         (GFilenameCompleter *completer,
+                                                               gboolean dirs_only);
+
+G_END_DECLS
+
+#endif /* __G_FILENAME_COMPLETER_H__ */
diff --git a/gio/gfileoutputstream.c b/gio/gfileoutputstream.c
new file mode 100644 (file)
index 0000000..40914d6
--- /dev/null
@@ -0,0 +1,613 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <gfileoutputstream.h>
+#include <gseekable.h>
+#include "gsimpleasyncresult.h"
+#include "glibintl.h"
+
+static void       g_file_output_stream_seekable_iface_init    (GSeekableIface       *iface);
+static goffset    g_file_output_stream_seekable_tell          (GSeekable            *seekable);
+static gboolean   g_file_output_stream_seekable_can_seek      (GSeekable            *seekable);
+static gboolean   g_file_output_stream_seekable_seek          (GSeekable            *seekable,
+                                                              goffset               offset,
+                                                              GSeekType             type,
+                                                              GCancellable         *cancellable,
+                                                              GError              **error);
+static gboolean   g_file_output_stream_seekable_can_truncate  (GSeekable            *seekable);
+static gboolean   g_file_output_stream_seekable_truncate      (GSeekable            *seekable,
+                                                              goffset               offset,
+                                                              GCancellable         *cancellable,
+                                                              GError              **error);
+static void       g_file_output_stream_real_query_info_async  (GFileOutputStream    *stream,
+                                                              char                 *attributes,
+                                                              int                   io_priority,
+                                                              GCancellable         *cancellable,
+                                                              GAsyncReadyCallback   callback,
+                                                              gpointer              user_data);
+static GFileInfo *g_file_output_stream_real_query_info_finish (GFileOutputStream    *stream,
+                                                              GAsyncResult         *result,
+                                                              GError              **error);
+
+G_DEFINE_TYPE_WITH_CODE (GFileOutputStream, g_file_output_stream, G_TYPE_OUTPUT_STREAM,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+                                               g_file_output_stream_seekable_iface_init));
+
+struct _GFileOutputStreamPrivate {
+  GAsyncReadyCallback outstanding_callback;
+};
+
+static void
+g_file_output_stream_class_init (GFileOutputStreamClass *klass)
+{
+  g_type_class_add_private (klass, sizeof (GFileOutputStreamPrivate));
+
+  klass->query_info_async = g_file_output_stream_real_query_info_async;
+  klass->query_info_finish = g_file_output_stream_real_query_info_finish;
+}
+
+static void
+g_file_output_stream_seekable_iface_init (GSeekableIface *iface)
+{
+  iface->tell = g_file_output_stream_seekable_tell;
+  iface->can_seek = g_file_output_stream_seekable_can_seek;
+  iface->seek = g_file_output_stream_seekable_seek;
+  iface->can_truncate = g_file_output_stream_seekable_can_truncate;
+  iface->truncate = g_file_output_stream_seekable_truncate;
+}
+
+static void
+g_file_output_stream_init (GFileOutputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                             G_TYPE_FILE_OUTPUT_STREAM,
+                                             GFileOutputStreamPrivate);
+}
+
+/**
+ * g_file_output_stream_query_info:
+ * @stream: a #GFileOutputStream.
+ * @attributes:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ *
+ *  * Returns: %NULL or a #GFileInfo for the @stream.
+ * 
+ * For the asynchronous version of this function, see 
+ * g_file_output_stream_query_info_async(). 
+ **/
+GFileInfo *
+g_file_output_stream_query_info (GFileOutputStream      *stream,
+                                   char                   *attributes,
+                                   GCancellable           *cancellable,
+                                   GError                **error)
+{
+  GFileOutputStreamClass *class;
+  GOutputStream *output_stream;
+  GFileInfo *info;
+  
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
+  
+  output_stream = G_OUTPUT_STREAM (stream);
+  
+  if (g_output_stream_is_closed (output_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return NULL;
+    }
+  
+  if (g_output_stream_has_pending (output_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return NULL;
+    }
+      
+  info = NULL;
+  
+  g_output_stream_set_pending (output_stream, TRUE);
+  
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+  if (class->query_info)
+    info = class->query_info (stream, attributes, cancellable, error);
+  else
+    g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                _("Stream doesn't support query_info"));
+  
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  g_output_stream_set_pending (output_stream, FALSE);
+  
+  return info;
+}
+
+static void
+async_ready_callback_wrapper (GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer      user_data)
+{
+  GFileOutputStream *stream = G_FILE_OUTPUT_STREAM (source_object);
+
+  g_output_stream_set_pending (G_OUTPUT_STREAM (stream), FALSE);
+  if (stream->priv->outstanding_callback)
+    (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+/**
+ * g_file_output_stream_query_info_async:
+ * @stream: a #GFileOutputStream.
+ * @attributes:
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: user data for @callback.
+ * 
+ * Asynchronously queries the @stream for a #GFileInfo. When completed,
+ * @callback will be called with a #GAsyncResult which can be used to 
+ * finish the operation with g_file_output_stream_query_info_finish().
+ * 
+ * For the synchronous version of this function, see 
+ * g_file_output_stream_query_info().
+ *
+ **/
+void
+g_file_output_stream_query_info_async (GFileOutputStream     *stream,
+                                         char                 *attributes,
+                                         int                   io_priority,
+                                         GCancellable         *cancellable,
+                                         GAsyncReadyCallback   callback,
+                                         gpointer              user_data)
+{
+  GFileOutputStreamClass *klass;
+  GOutputStream *output_stream;
+
+  g_return_if_fail (G_IS_FILE_OUTPUT_STREAM (stream));
+
+  output_stream = G_OUTPUT_STREAM (stream);
+  if (g_output_stream_is_closed (output_stream))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+  
+  if (g_output_stream_has_pending (output_stream))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  klass = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+
+  g_output_stream_set_pending (output_stream, TRUE);
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  klass->query_info_async (stream, attributes, io_priority, cancellable,
+                             async_ready_callback_wrapper, user_data);
+}
+
+/**
+ * g_file_output_stream_query_info_finish:
+ * @stream: a #GFileOutputStream.
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * 
+ * Finalizes the asynchronous query started 
+ * by g_file_output_stream_query_info_async().
+ * 
+ * Returns: A #GFileInfo for the finished query.
+ **/
+GFileInfo *
+g_file_output_stream_query_info_finish (GFileOutputStream     *stream,
+                                          GAsyncResult         *result,
+                                          GError              **error)
+{
+  GSimpleAsyncResult *simple;
+  GFileOutputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+  
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+  return class->query_info_finish (stream, result, error);
+}
+
+/**
+ * g_file_output_stream_get_etag:
+ * @stream: a #GFileOutputString.
+ * 
+ * Returns: 
+ **/
+char *
+g_file_output_stream_get_etag (GFileOutputStream  *stream)
+{
+  GFileOutputStreamClass *class;
+  GOutputStream *output_stream;
+  char *etag;
+  
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), NULL);
+  
+  output_stream = G_OUTPUT_STREAM (stream);
+  
+  if (!g_output_stream_is_closed (output_stream))
+    {
+      g_warning ("stream is not closed yet, can't get etag");
+      return NULL;
+    }
+
+  etag = NULL;
+  
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+  if (class->get_etag)
+    etag = class->get_etag (stream);
+  
+  return etag;
+}
+
+/**
+ * g_file_output_stream_tell:
+ * @stream:
+ * 
+ * Returns: 
+ **/
+goffset
+g_file_output_stream_tell (GFileOutputStream  *stream)
+{
+  GFileOutputStreamClass *class;
+  goffset offset;
+  
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), 0);  
+
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+
+  offset = 0;
+  if (class->tell)
+    offset = class->tell (stream);
+
+  return offset;
+}
+
+static goffset
+g_file_output_stream_seekable_tell (GSeekable *seekable)
+{
+  return g_file_output_stream_tell (G_FILE_OUTPUT_STREAM (seekable));
+}
+
+/**
+ * g_file_output_stream_can_seek:
+ * @stream:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_file_output_stream_can_seek (GFileOutputStream  *stream)
+{
+  GFileOutputStreamClass *class;
+  gboolean can_seek;
+
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
+
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+
+  can_seek = FALSE;
+  if (class->seek)
+    {
+      can_seek = TRUE;
+      if (class->can_seek)
+       can_seek = class->can_seek (stream);
+    }
+  
+  return can_seek;
+}
+
+static gboolean
+g_file_output_stream_seekable_can_seek (GSeekable *seekable)
+{
+  return g_file_output_stream_can_seek (G_FILE_OUTPUT_STREAM (seekable));
+}
+
+/**
+ * g_file_output_stream_seek:
+ * @stream:
+ * @offset:
+ * @type:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+gboolean
+g_file_output_stream_seek (GFileOutputStream  *stream,
+                          goffset             offset,
+                          GSeekType           type,
+                          GCancellable       *cancellable,
+                          GError            **error)
+{
+  GFileOutputStreamClass *class;
+  GOutputStream *output_stream;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
+
+  output_stream = G_OUTPUT_STREAM (stream);
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+
+  if (g_output_stream_is_closed (output_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return FALSE;
+    }
+  
+  if (g_output_stream_has_pending (output_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return FALSE;
+    }
+  
+  if (!class->seek)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Seek not supported on stream"));
+      return FALSE;
+    }
+
+  g_output_stream_set_pending (output_stream, TRUE);
+  
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  res = class->seek (stream, offset, type, cancellable, error);
+  
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+
+  g_output_stream_set_pending (output_stream, FALSE);
+  
+  return res;
+}
+
+static gboolean
+g_file_output_stream_seekable_seek (GSeekable  *seekable,
+                                   goffset     offset,
+                                   GSeekType   type,
+                                   GCancellable  *cancellable,
+                                   GError    **error)
+{
+  return g_file_output_stream_seek (G_FILE_OUTPUT_STREAM (seekable),
+                                   offset, type, cancellable, error);
+}
+
+/**
+ * g_file_output_stream_can_truncate:
+ * @stream:
+ * 
+ * Returns: %TRUE if stream can be truncated.
+ **/
+gboolean
+g_file_output_stream_can_truncate (GFileOutputStream  *stream)
+{
+  GFileOutputStreamClass *class;
+  gboolean can_truncate;
+
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
+
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+
+  can_truncate = FALSE;
+  if (class->truncate)
+    {
+      can_truncate = TRUE;
+      if (class->can_truncate)
+       can_truncate = class->can_truncate (stream);
+    }
+  
+  return can_truncate;
+}
+
+static gboolean
+g_file_output_stream_seekable_can_truncate (GSeekable  *seekable)
+{
+  return g_file_output_stream_can_truncate (G_FILE_OUTPUT_STREAM (seekable));
+}
+
+/**
+ * g_file_output_stream_truncate:
+ * @stream:
+ * @size:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: %TRUE if @stream is truncated.
+ **/
+gboolean
+g_file_output_stream_truncate (GFileOutputStream  *stream,
+                              goffset             size,
+                              GCancellable       *cancellable,
+                              GError            **error)
+{
+  GFileOutputStreamClass *class;
+  GOutputStream *output_stream;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_FILE_OUTPUT_STREAM (stream), FALSE);
+
+  output_stream = G_OUTPUT_STREAM (stream);
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (stream);
+
+  if (g_output_stream_is_closed (output_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return FALSE;
+    }
+  
+  if (g_output_stream_has_pending (output_stream))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return FALSE;
+    }
+  
+  if (!class->truncate)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Truncate not supported on stream"));
+      return FALSE;
+    }
+
+  g_output_stream_set_pending (output_stream, TRUE);
+  
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  res = class->truncate (stream, size, cancellable, error);
+  
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+
+  g_output_stream_set_pending (output_stream, FALSE);
+  
+  return res;
+}
+
+static gboolean
+g_file_output_stream_seekable_truncate (GSeekable     *seekable,
+                                       goffset        size,
+                                       GCancellable  *cancellable,
+                                       GError       **error)
+{
+  return g_file_output_stream_truncate (G_FILE_OUTPUT_STREAM (seekable),
+                                       size, cancellable, error);
+}
+/********************************************
+ *   Default implementation of async ops    *
+ ********************************************/
+
+typedef struct {
+  char *attributes;
+  GFileInfo *info;
+} QueryInfoAsyncData;
+
+static void
+query_info_data_free (QueryInfoAsyncData *data)
+{
+  if (data->info)
+    g_object_unref (data->info);
+  g_free (data->attributes);
+  g_free (data);
+}
+
+static void
+query_info_async_thread (GSimpleAsyncResult *res,
+                      GObject *object,
+                      GCancellable *cancellable)
+{
+  GFileOutputStreamClass *class;
+  GError *error = NULL;
+  QueryInfoAsyncData *data;
+  GFileInfo *info;
+  
+  data = g_simple_async_result_get_op_res_gpointer (res);
+
+  info = NULL;
+  
+  class = G_FILE_OUTPUT_STREAM_GET_CLASS (object);
+  if (class->query_info)
+    info = class->query_info (G_FILE_OUTPUT_STREAM (object), data->attributes, cancellable, &error);
+  else
+    g_set_error (&error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                _("Stream doesn't support query_info"));
+
+  if (info == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    data->info = info;
+}
+
+static void
+g_file_output_stream_real_query_info_async (GFileOutputStream     *stream,
+                                              char                 *attributes,
+                                              int                   io_priority,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data)
+{
+  GSimpleAsyncResult *res;
+  QueryInfoAsyncData *data;
+
+  data = g_new0 (QueryInfoAsyncData, 1);
+  data->attributes = g_strdup (attributes);
+  
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_file_output_stream_real_query_info_async);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify)query_info_data_free);
+  
+  g_simple_async_result_run_in_thread (res, query_info_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static GFileInfo *
+g_file_output_stream_real_query_info_finish (GFileOutputStream     *stream,
+                                               GAsyncResult         *res,
+                                               GError              **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  QueryInfoAsyncData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_file_output_stream_real_query_info_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  if (data->info)
+    return g_object_ref (data->info);
+  
+  return NULL;
+}
diff --git a/gio/gfileoutputstream.h b/gio/gfileoutputstream.h
new file mode 100644 (file)
index 0000000..6525754
--- /dev/null
@@ -0,0 +1,121 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_FILE_OUTPUT_STREAM_H__
+#define __G_FILE_OUTPUT_STREAM_H__
+
+#include <gio/goutputstream.h>
+#include <gio/gfileinfo.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILE_OUTPUT_STREAM         (g_file_output_stream_get_type ())
+#define G_FILE_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILE_OUTPUT_STREAM, GFileOutputStream))
+#define G_FILE_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILE_OUTPUT_STREAM, GFileOutputStreamClass))
+#define G_IS_FILE_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILE_OUTPUT_STREAM))
+#define G_IS_FILE_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILE_OUTPUT_STREAM))
+#define G_FILE_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILE_OUTPUT_STREAM, GFileOutputStreamClass))
+
+typedef struct _GFileOutputStream         GFileOutputStream;
+typedef struct _GFileOutputStreamClass    GFileOutputStreamClass;
+typedef struct _GFileOutputStreamPrivate  GFileOutputStreamPrivate;
+
+struct _GFileOutputStream
+{
+  GOutputStream parent;
+  
+  /*< private >*/
+  GFileOutputStreamPrivate *priv;
+};
+
+struct _GFileOutputStreamClass
+{
+  GOutputStreamClass parent_class;
+
+  goffset    (*tell)          (GFileOutputStream     *stream);
+  gboolean   (*can_seek)      (GFileOutputStream     *stream);
+  gboolean   (*seek)         (GFileOutputStream     *stream,
+                              goffset               offset,
+                              GSeekType             type,
+                              GCancellable         *cancellable,
+                              GError              **error);
+  gboolean   (*can_truncate)  (GFileOutputStream    *stream);
+  gboolean   (*truncate)      (GFileOutputStream    *stream,
+                              goffset               size,
+                              GCancellable         *cancellable,
+                              GError              **error);
+  GFileInfo *(*query_info)    (GFileOutputStream    *stream,
+                              char                 *attributes,
+                              GCancellable         *cancellable,
+                              GError              **error);
+  void       (*query_info_async)  (GFileOutputStream     *stream,
+                                  char                 *attributes,
+                                  int                   io_priority,
+                                  GCancellable         *cancellable,
+                                  GAsyncReadyCallback   callback,
+                                  gpointer              user_data);
+  GFileInfo *(*query_info_finish) (GFileOutputStream     *stream,
+                                  GAsyncResult         *res,
+                                  GError              **error);
+  char      *(*get_etag)      (GFileOutputStream    *stream);
+    
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_file_output_stream_get_type (void) G_GNUC_CONST;
+
+
+GFileInfo *g_file_output_stream_query_info (GFileOutputStream  *stream,
+                                           char               *attributes,
+                                           GCancellable       *cancellable,
+                                           GError            **error);
+void       g_file_output_stream_query_info_async  (GFileOutputStream     *stream,
+                                                  char                 *attributes,
+                                                  int                   io_priority,
+                                                  GCancellable         *cancellable,
+                                                  GAsyncReadyCallback   callback,
+                                                  gpointer              user_data);
+GFileInfo *g_file_output_stream_query_info_finish (GFileOutputStream     *stream,
+                                                  GAsyncResult         *result,
+                                                  GError              **error);
+char *     g_file_output_stream_get_etag      (GFileOutputStream  *stream);
+goffset    g_file_output_stream_tell          (GFileOutputStream  *stream);
+gboolean   g_file_output_stream_can_seek      (GFileOutputStream  *stream);
+gboolean   g_file_output_stream_seek          (GFileOutputStream  *stream,
+                                              goffset             offset,
+                                              GSeekType           type,
+                                              GCancellable       *cancellable,
+                                              GError            **error);
+gboolean   g_file_output_stream_can_truncate  (GFileOutputStream  *stream);
+gboolean   g_file_output_stream_truncate      (GFileOutputStream  *stream,
+                                              goffset             size,
+                                              GCancellable       *cancellable,
+                                              GError            **error);
+
+G_END_DECLS
+
+#endif /* __G_FILE_FILE_OUTPUT_STREAM_H__ */
diff --git a/gio/gfilterinputstream.c b/gio/gfilterinputstream.c
new file mode 100644 (file)
index 0000000..6d2f99a
--- /dev/null
@@ -0,0 +1,391 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#include <config.h>
+#include "gfilterinputstream.h"
+#include "ginputstream.h"
+#include "glibintl.h"
+
+enum {
+  PROP_0,
+  PROP_BASE_STREAM
+};
+
+static void     g_filter_input_stream_set_property (GObject      *object,
+                                                    guint         prop_id,
+                                                    const GValue *value,
+                                                    GParamSpec   *pspec);
+
+static void     g_filter_input_stream_get_property (GObject      *object,
+                                                    guint         prop_id,
+                                                    GValue       *value,
+                                                    GParamSpec   *pspec);
+static void     g_filter_input_stream_finalize     (GObject *object);
+
+
+static gssize   g_filter_input_stream_read         (GInputStream         *stream,
+                                                    void                 *buffer,
+                                                    gsize                 count,
+                                                    GCancellable         *cancellable,
+                                                    GError              **error);
+static gssize   g_filter_input_stream_skip         (GInputStream         *stream,
+                                                    gsize                 count,
+                                                    GCancellable         *cancellable,
+                                                    GError              **error);
+static gboolean g_filter_input_stream_close        (GInputStream         *stream,
+                                                    GCancellable         *cancellable,
+                                                    GError              **error);
+static void     g_filter_input_stream_read_async   (GInputStream         *stream,
+                                                    void                 *buffer,
+                                                    gsize                 count,
+                                                    int                   io_priority,
+                                                    GCancellable         *cancellable,
+                                                    GAsyncReadyCallback   callback,
+                                                    gpointer              user_data);
+static gssize   g_filter_input_stream_read_finish  (GInputStream         *stream,
+                                                    GAsyncResult         *result,
+                                                    GError              **error);
+static void     g_filter_input_stream_skip_async   (GInputStream         *stream,
+                                                    gsize                 count,
+                                                    int                   io_priority,
+                                                    GCancellable         *cancellabl,
+                                                    GAsyncReadyCallback   callback,
+                                                    gpointer              datae);
+static gssize   g_filter_input_stream_skip_finish  (GInputStream         *stream,
+                                                    GAsyncResult         *result,
+                                                    GError              **error);
+static void     g_filter_input_stream_close_async  (GInputStream         *stream,
+                                                    int                   io_priority,
+                                                    GCancellable         *cancellabl,
+                                                    GAsyncReadyCallback   callback,
+                                                    gpointer              data);
+static gboolean g_filter_input_stream_close_finish (GInputStream         *stream,
+                                                    GAsyncResult         *result,
+                                                    GError              **error);
+
+G_DEFINE_TYPE (GFilterInputStream, g_filter_input_stream, G_TYPE_INPUT_STREAM)
+
+
+static void
+g_filter_input_stream_class_init (GFilterInputStreamClass *klass)
+{
+  GObjectClass *object_class;
+  GInputStreamClass *istream_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = g_filter_input_stream_get_property;
+  object_class->set_property = g_filter_input_stream_set_property;
+  object_class->finalize     = g_filter_input_stream_finalize;
+
+  istream_class = G_INPUT_STREAM_CLASS (klass);
+  istream_class->read  = g_filter_input_stream_read;
+  istream_class->skip  = g_filter_input_stream_skip;
+  istream_class->close = g_filter_input_stream_close;
+
+  istream_class->read_async   = g_filter_input_stream_read_async;
+  istream_class->read_finish  = g_filter_input_stream_read_finish;
+  istream_class->skip_async   = g_filter_input_stream_skip_async;
+  istream_class->skip_finish  = g_filter_input_stream_skip_finish;
+  istream_class->close_async  = g_filter_input_stream_close_async;
+  istream_class->close_finish = g_filter_input_stream_close_finish;
+
+  g_object_class_install_property (object_class,
+                                   PROP_BASE_STREAM,
+                                   g_param_spec_object ("base-stream",
+                                                         P_("The Filter Base Stream"),
+                                                         P_("The underlying base stream the io ops will be done on"),
+                                                         G_TYPE_INPUT_STREAM,
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
+                                                         G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+}
+
+static void
+g_filter_input_stream_set_property (GObject         *object,
+                                    guint            prop_id,
+                                    const GValue    *value,
+                                    GParamSpec      *pspec)
+{
+  GFilterInputStream *filter_stream;
+  GObject *obj;
+
+  filter_stream = G_FILTER_INPUT_STREAM (object);
+
+  switch (prop_id) 
+    {
+    case PROP_BASE_STREAM:
+      obj = g_value_dup_object (value);
+      filter_stream->base_stream = G_INPUT_STREAM (obj); 
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_filter_input_stream_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+  GFilterInputStream *filter_stream;
+
+  filter_stream = G_FILTER_INPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_STREAM:
+      g_value_set_object (value, filter_stream->base_stream);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_filter_input_stream_finalize (GObject *object)
+{
+  GFilterInputStream *stream;
+
+  stream = G_FILTER_INPUT_STREAM (object);
+
+  g_object_unref (stream->base_stream);
+
+  if (G_OBJECT_CLASS (g_filter_input_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_filter_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_filter_input_stream_init (GFilterInputStream *stream)
+{
+
+}
+
+/**
+ * g_filter_input_stream_get_base_stream:
+ * @stream: a #GFilterInputStream.
+ * 
+ * Returns: a #GInputStream.
+ **/
+GInputStream *
+g_filter_input_stream_get_base_stream (GFilterInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_FILTER_INPUT_STREAM (stream), NULL);
+
+  return stream->base_stream;
+}
+
+static gssize
+g_filter_input_stream_read (GInputStream *stream,
+                            void         *buffer,
+                            gsize         count,
+                            GCancellable *cancellable,
+                            GError      **error)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+  gssize              nread;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  nread = g_input_stream_read (base_stream,
+                               buffer,
+                               count,
+                               cancellable,
+                               error);
+
+  return nread;
+}
+
+static gssize
+g_filter_input_stream_skip (GInputStream              *stream,
+                            gsize                      count,
+                            GCancellable              *cancellable,
+                            GError                   **error)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+  gssize              nskipped;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  nskipped = g_input_stream_skip (base_stream,
+                                  count,
+                                  cancellable,
+                                  error);
+  return nskipped;
+}
+
+static gboolean
+g_filter_input_stream_close (GInputStream  *stream,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+  gboolean            res;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  res = g_input_stream_close (base_stream,
+                              cancellable,
+                              error);
+
+  return res;
+}
+
+static void
+g_filter_input_stream_read_async (GInputStream           *stream,
+                                  void                   *buffer,
+                                  gsize                   count,
+                                  int                     io_priority,
+                                  GCancellable           *cancellable,
+                                  GAsyncReadyCallback     callback,
+                                  gpointer                user_data)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  g_input_stream_read_async (base_stream,
+                             buffer,
+                             count,
+                             io_priority,
+                             cancellable,
+                             callback,
+                             user_data);
+}
+
+static gssize
+g_filter_input_stream_read_finish (GInputStream  *stream,
+                                   GAsyncResult  *result,
+                                   GError       **error)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+  gssize nread;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  nread = g_input_stream_read_finish (base_stream,
+                                      result,
+                                      error);
+  
+  return nread;
+}
+
+static void
+g_filter_input_stream_skip_async (GInputStream        *stream,
+                                  gsize                count,
+                                  int                  io_priority,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  g_input_stream_skip_async (base_stream,
+                             count,
+                             io_priority,
+                             cancellable,
+                             callback,
+                             user_data);
+
+}
+
+static gssize
+g_filter_input_stream_skip_finish (GInputStream  *stream,
+                                   GAsyncResult  *result,
+                                   GError       **error)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+  gssize nskipped;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  nskipped = g_input_stream_skip_finish (base_stream,
+                                         result,
+                                         error);
+
+  return nskipped;
+}
+
+static void
+g_filter_input_stream_close_async (GInputStream         *stream,
+                                   int                   io_priority,
+                                   GCancellable         *cancellable,
+                                   GAsyncReadyCallback   callback,
+                                   gpointer              user_data)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  g_input_stream_close_async (base_stream,
+                              io_priority,
+                              cancellable,
+                              callback,
+                              user_data);
+
+
+}
+
+static gboolean
+g_filter_input_stream_close_finish (GInputStream  *stream,
+                                    GAsyncResult  *result,
+                                    GError       **error)
+{
+  GFilterInputStream *filter_stream;
+  GInputStream       *base_stream;
+  gboolean res;
+
+  filter_stream = G_FILTER_INPUT_STREAM (stream);
+  base_stream = filter_stream->base_stream;
+
+  res = g_input_stream_close_finish (stream,
+                                     result,
+                                     error);
+
+  return res;
+}
+
+/* vim: ts=2 sw=2 et */
diff --git a/gio/gfilterinputstream.h b/gio/gfilterinputstream.h
new file mode 100644 (file)
index 0000000..331f9be
--- /dev/null
@@ -0,0 +1,65 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#ifndef __G_FILTER_INPUT_STREAM_H__
+#define __G_FILTER_INPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/ginputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILTER_INPUT_STREAM         (g_filter_input_stream_get_type ())
+#define G_FILTER_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILTER_INPUT_STREAM, GFilterInputStream))
+#define G_FILTER_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILTER_INPUT_STREAM, GFilterInputStreamClass))
+#define G_IS_FILTER_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILTER_INPUT_STREAM))
+#define G_IS_FILTER_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILTER_INPUT_STREAM))
+#define G_FILTER_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILTER_INPUT_STREAM, GFilterInputStreamClass))
+
+typedef struct _GFilterInputStream         GFilterInputStream;
+typedef struct _GFilterInputStreamClass    GFilterInputStreamClass;
+typedef struct _GFilterInputStreamPrivate  GFilterInputStreamPrivate;
+
+struct _GFilterInputStream
+{
+  GInputStream parent;
+
+  /*<protected >*/
+  GInputStream *base_stream;
+};
+
+struct _GFilterInputStreamClass
+{
+  GInputStreamClass parent_class;
+  
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+};
+
+
+GType          g_filter_input_stream_get_type  (void) G_GNUC_CONST;
+GInputStream  *g_filter_input_stream_get_base_stream (GFilterInputStream *stream);
+G_END_DECLS
+
+#endif /* __G_FILTER_INPUT_STREAM_H__ */
diff --git a/gio/gfilteroutputstream.c b/gio/gfilteroutputstream.c
new file mode 100644 (file)
index 0000000..ca41be5
--- /dev/null
@@ -0,0 +1,366 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#include <config.h>
+#include "gfilteroutputstream.h"
+#include "goutputstream.h"
+#include "glibintl.h"
+
+enum {
+  PROP_0,
+  PROP_BASE_STREAM
+};
+
+static void     g_filter_output_stream_set_property (GObject      *object,
+                                                     guint         prop_id,
+                                                     const GValue *value,
+                                                     GParamSpec   *pspec);
+
+static void     g_filter_output_stream_get_property (GObject    *object,
+                                                     guint       prop_id,
+                                                     GValue     *value,
+                                                     GParamSpec *pspec);
+static void     g_filter_output_stream_dispose      (GObject *object);
+
+
+static gssize   g_filter_output_stream_write        (GOutputStream *stream,
+                                                     const void    *buffer,
+                                                     gsize          count,
+                                                     GCancellable  *cancellable,
+                                                     GError       **error);
+static gboolean g_filter_output_stream_flush        (GOutputStream    *stream,
+                                                     GCancellable  *cancellable,
+                                                     GError          **error);
+static gboolean g_filter_output_stream_close        (GOutputStream  *stream,
+                                                     GCancellable   *cancellable,
+                                                     GError        **error);
+static void     g_filter_output_stream_write_async  (GOutputStream        *stream,
+                                                     const void           *buffer,
+                                                     gsize                 count,
+                                                     int                   io_priority,
+                                                     GCancellable         *cancellable,
+                                                     GAsyncReadyCallback   callback,
+                                                     gpointer              data);
+static gssize   g_filter_output_stream_write_finish (GOutputStream        *stream,
+                                                     GAsyncResult         *result,
+                                                     GError              **error);
+static void     g_filter_output_stream_flush_async  (GOutputStream        *stream,
+                                                     int                   io_priority,
+                                                     GCancellable         *cancellable,
+                                                     GAsyncReadyCallback   callback,
+                                                     gpointer              data);
+static gboolean g_filter_output_stream_flush_finish (GOutputStream        *stream,
+                                                     GAsyncResult         *result,
+                                                     GError              **error);
+static void     g_filter_output_stream_close_async  (GOutputStream        *stream,
+                                                     int                   io_priority,
+                                                     GCancellable         *cancellable,
+                                                     GAsyncReadyCallback   callback,
+                                                     gpointer              data);
+static gboolean g_filter_output_stream_close_finish (GOutputStream        *stream,
+                                                     GAsyncResult         *result,
+                                                     GError              **error);
+
+
+
+G_DEFINE_TYPE (GFilterOutputStream, g_filter_output_stream, G_TYPE_OUTPUT_STREAM)
+
+
+
+static void
+g_filter_output_stream_class_init (GFilterOutputStreamClass *klass)
+{
+  GObjectClass *object_class;
+  GOutputStreamClass *ostream_class;
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->get_property = g_filter_output_stream_get_property;
+  object_class->set_property = g_filter_output_stream_set_property;
+  object_class->dispose      = g_filter_output_stream_dispose;
+    
+  ostream_class = G_OUTPUT_STREAM_CLASS (klass);
+  ostream_class->write = g_filter_output_stream_write;
+  ostream_class->flush = g_filter_output_stream_flush;
+  ostream_class->close = g_filter_output_stream_close;
+  ostream_class->write_async  = g_filter_output_stream_write_async;
+  ostream_class->write_finish = g_filter_output_stream_write_finish;
+  ostream_class->flush_async  = g_filter_output_stream_flush_async;
+  ostream_class->flush_finish = g_filter_output_stream_flush_finish;
+  ostream_class->close_async  = g_filter_output_stream_close_async;
+  ostream_class->close_finish = g_filter_output_stream_close_finish;
+
+  g_object_class_install_property (object_class,
+                                   PROP_BASE_STREAM,
+                                   g_param_spec_object ("base-stream",
+                                                         P_("The Filter Base Stream"),
+                                                         P_("The underlying base stream the io ops will be done on"),
+                                                         G_TYPE_OUTPUT_STREAM,
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | 
+                                                         G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+}
+
+static void
+g_filter_output_stream_set_property (GObject         *object,
+                                     guint            prop_id,
+                                     const GValue    *value,
+                                     GParamSpec      *pspec)
+{
+  GFilterOutputStream *filter_stream;
+  GObject *obj;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (object);
+
+  switch (prop_id) 
+    {
+    case PROP_BASE_STREAM:
+      obj = g_value_dup_object (value);
+      filter_stream->base_stream = G_OUTPUT_STREAM (obj);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_filter_output_stream_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GFilterOutputStream *filter_stream;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_STREAM:
+      g_value_set_object (value, filter_stream->base_stream);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+
+}
+
+static void
+g_filter_output_stream_dispose (GObject *object)
+{
+  GFilterOutputStream        *stream;
+
+  stream = G_FILTER_OUTPUT_STREAM (object);
+
+  G_OBJECT_CLASS (g_filter_output_stream_parent_class)->dispose (object);
+  
+  if (stream->base_stream)
+    {
+      g_object_unref (stream->base_stream);
+      stream->base_stream = NULL;
+    }
+}
+
+
+static void
+g_filter_output_stream_init (GFilterOutputStream *stream)
+{
+}
+
+GOutputStream *
+g_filter_output_stream_get_base_stream (GFilterOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_FILTER_OUTPUT_STREAM (stream), NULL);
+
+  return stream->base_stream;
+}
+
+static gssize
+g_filter_output_stream_write (GOutputStream *stream,
+                              const void *buffer,
+                              gsize          count,
+                              GCancellable  *cancellable,
+                              GError       **error)
+{
+  GFilterOutputStream *filter_stream;
+  gssize nwritten;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  nwritten = g_output_stream_write (filter_stream->base_stream,
+                                    buffer,
+                                    count,
+                                    cancellable,
+                                    error);
+
+  return nwritten;
+}
+
+static gboolean
+g_filter_output_stream_flush (GOutputStream    *stream,
+                              GCancellable  *cancellable,
+                              GError          **error)
+{
+  GFilterOutputStream *filter_stream;
+  gboolean res;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  res = g_output_stream_flush (filter_stream->base_stream,
+                               cancellable,
+                               error);
+
+  return res;
+}
+
+static gboolean
+g_filter_output_stream_close (GOutputStream  *stream,
+                              GCancellable   *cancellable,
+                              GError        **error)
+{
+  GFilterOutputStream *filter_stream;
+  gboolean res;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  res = g_output_stream_close (filter_stream->base_stream,
+                               cancellable,
+                               error);
+
+  return res;
+}
+
+static void
+g_filter_output_stream_write_async (GOutputStream        *stream,
+                                    const void           *buffer,
+                                    gsize                 count,
+                                    int                   io_priority,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback   callback,
+                                    gpointer              data)
+{
+  GFilterOutputStream *filter_stream;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  g_output_stream_write_async (filter_stream->base_stream,
+                               buffer,
+                               count,
+                               io_priority,
+                               cancellable,
+                               callback,
+                               data);
+
+}
+
+static gssize
+g_filter_output_stream_write_finish (GOutputStream        *stream,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+  GFilterOutputStream *filter_stream;
+  gssize nwritten;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  nwritten = g_output_stream_write_finish (filter_stream->base_stream,
+                                           result,
+                                           error);
+
+  return nwritten;
+}
+
+static void
+g_filter_output_stream_flush_async (GOutputStream        *stream,
+                                    int                   io_priority,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback   callback,
+                                    gpointer              data)
+{
+  GFilterOutputStream *filter_stream;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  g_output_stream_flush_async (filter_stream->base_stream,
+                               io_priority,
+                               cancellable,
+                               callback,
+                               data);
+}
+
+static gboolean
+g_filter_output_stream_flush_finish (GOutputStream        *stream,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+  GFilterOutputStream *filter_stream;
+  gboolean res;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  res = g_output_stream_flush_finish (filter_stream->base_stream,
+                                      result,
+                                      error);
+
+  return res;
+}
+
+static void
+g_filter_output_stream_close_async (GOutputStream        *stream,
+                                    int                   io_priority,
+                                    GCancellable         *cancellable,
+                                    GAsyncReadyCallback   callback,
+                                    gpointer              data)
+{
+  GFilterOutputStream *filter_stream;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  g_output_stream_close_async (filter_stream->base_stream,
+                               io_priority,
+                               cancellable,
+                               callback,
+                               data);
+}
+
+static gboolean
+g_filter_output_stream_close_finish (GOutputStream        *stream,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+  GFilterOutputStream *filter_stream;
+  gboolean res;
+
+  filter_stream = G_FILTER_OUTPUT_STREAM (stream);
+
+  res = g_output_stream_close_finish (filter_stream->base_stream,
+                                      result,
+                                      error);
+
+  return res;
+}
+
+
+/* vim: ts=2 sw=2 et */
diff --git a/gio/gfilteroutputstream.h b/gio/gfilteroutputstream.h
new file mode 100644 (file)
index 0000000..604f928
--- /dev/null
@@ -0,0 +1,65 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#ifndef __G_FILTER_OUTPUT_STREAM_H__
+#define __G_FILTER_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/goutputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_FILTER_OUTPUT_STREAM         (g_filter_output_stream_get_type ())
+#define G_FILTER_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_FILTER_OUTPUT_STREAM, GFilterOutputStream))
+#define G_FILTER_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_FILTER_OUTPUT_STREAM, GFilterOutputStreamClass))
+#define G_IS_FILTER_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_FILTER_OUTPUT_STREAM))
+#define G_IS_FILTER_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_FILTER_OUTPUT_STREAM))
+#define G_FILTER_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_FILTER_OUTPUT_STREAM, GFilterOutputStreamClass))
+
+typedef struct _GFilterOutputStream         GFilterOutputStream;
+typedef struct _GFilterOutputStreamClass    GFilterOutputStreamClass;
+typedef struct _GFilterOutputStreamPrivate  GFilterOutputStreamPrivate;
+
+struct _GFilterOutputStream
+{
+  GOutputStream parent;
+
+  /*< protected >*/
+  GOutputStream *base_stream;
+};
+
+struct _GFilterOutputStreamClass
+{
+ GOutputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+};
+
+
+GType           g_filter_output_stream_get_type  (void) G_GNUC_CONST;
+GOutputStream  *g_filter_output_stream_get_base_stream (GFilterOutputStream *stream);
+G_END_DECLS
+
+#endif /* __G_FILTER_OUTPUT_STREAM_H__ */
diff --git a/gio/gicon.c b/gio/gicon.c
new file mode 100644 (file)
index 0000000..5913ac3
--- /dev/null
@@ -0,0 +1,118 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gicon.h"
+
+#include "glibintl.h"
+
+static void g_icon_base_init (gpointer g_class);
+static void g_icon_class_init (gpointer g_class,
+                              gpointer class_data);
+
+GType
+g_icon_get_type (void)
+{
+  static GType icon_type = 0;
+
+  if (! icon_type)
+    {
+      static const GTypeInfo icon_info =
+      {
+        sizeof (GIconIface), /* class_size */
+       g_icon_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       g_icon_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      icon_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GIcon"),
+                               &icon_info, 0);
+
+      g_type_interface_add_prerequisite (icon_type, G_TYPE_OBJECT);
+    }
+
+  return icon_type;
+}
+
+static void
+g_icon_class_init (gpointer g_class,
+                  gpointer class_data)
+{
+}
+
+static void
+g_icon_base_init (gpointer g_class)
+{
+}
+
+/**
+ * g_icon_hash:
+ * @icon: #gconstpointer to an icon object.
+ * 
+ * Returns: a #guint containing a hash for the @icon, suitable for 
+ * use in a #GHashTable or similar data structure.
+ **/
+guint
+g_icon_hash (gconstpointer icon)
+{
+  GIconIface *iface;
+
+  g_return_val_if_fail (G_IS_ICON (icon), 0);
+
+  iface = G_ICON_GET_IFACE (icon);
+
+  return (* iface->hash) ((GIcon *)icon);
+}
+
+/**
+ * g_icon_equal:
+ * @icon1: pointer to the first #GIcon.
+ * @icon2: pointer to the second #GIcon.
+ * 
+ * Returns: %TRUE if @icon1 is equal to @icon2. %FALSE otherwise.
+ **/
+gboolean
+g_icon_equal (GIcon *icon1,
+             GIcon *icon2)
+{
+  GIconIface *iface;
+
+  if (icon1 == NULL && icon2 == NULL)
+    return TRUE;
+
+  if (icon1 == NULL || icon2 == NULL)
+    return FALSE;
+  
+  if (G_TYPE_FROM_INSTANCE (icon1) != G_TYPE_FROM_INSTANCE (icon2))
+    return FALSE;
+
+  iface = G_ICON_GET_IFACE (icon1);
+  
+  return (* iface->equal) (icon1, icon2);
+}
+
diff --git a/gio/gicon.h b/gio/gicon.h
new file mode 100644 (file)
index 0000000..6c0dbc0
--- /dev/null
@@ -0,0 +1,58 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_ICON_H__
+#define __G_ICON_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_ICON            (g_icon_get_type ())
+#define G_ICON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_ICON, GIcon))
+#define G_IS_ICON(obj)        (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_ICON))
+#define G_ICON_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_ICON, GIconIface))
+
+typedef struct _GIcon                  GIcon; /* Dummy typedef */
+typedef struct _GIconIface             GIconIface;
+
+
+struct _GIconIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  guint               (*hash)               (GIcon                *icon);
+  gboolean            (*equal)              (GIcon                *icon1,
+                                            GIcon                *icon2);
+};
+
+GType g_icon_get_type (void) G_GNUC_CONST;
+
+guint    g_icon_hash  (gconstpointer  icon);
+gboolean g_icon_equal (GIcon         *icon1,
+                      GIcon         *icon2);
+
+G_END_DECLS
+
+#endif /* __G_ICON_H__ */
diff --git a/gio/ginputstream.c b/gio/ginputstream.c
new file mode 100644 (file)
index 0000000..127f520
--- /dev/null
@@ -0,0 +1,1184 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <glib.h>
+#include "glibintl.h"
+
+#include "ginputstream.h"
+#include "gseekable.h"
+#include "gsimpleasyncresult.h"
+
+G_DEFINE_TYPE (GInputStream, g_input_stream, G_TYPE_OBJECT);
+
+struct _GInputStreamPrivate {
+  guint closed : 1;
+  guint pending : 1;
+  GAsyncReadyCallback outstanding_callback;
+};
+
+static gssize   g_input_stream_real_skip         (GInputStream         *stream,
+                                                 gsize                 count,
+                                                 GCancellable         *cancellable,
+                                                 GError              **error);
+static void     g_input_stream_real_read_async   (GInputStream         *stream,
+                                                 void                 *buffer,
+                                                 gsize                 count,
+                                                 int                   io_priority,
+                                                 GCancellable         *cancellable,
+                                                 GAsyncReadyCallback   callback,
+                                                 gpointer              user_data);
+static gssize   g_input_stream_real_read_finish  (GInputStream         *stream,
+                                                 GAsyncResult         *result,
+                                                 GError              **error);
+static void     g_input_stream_real_skip_async   (GInputStream         *stream,
+                                                 gsize                 count,
+                                                 int                   io_priority,
+                                                 GCancellable         *cancellable,
+                                                 GAsyncReadyCallback   callback,
+                                                 gpointer              data);
+static gssize   g_input_stream_real_skip_finish  (GInputStream         *stream,
+                                                 GAsyncResult         *result,
+                                                 GError              **error);
+static void     g_input_stream_real_close_async  (GInputStream         *stream,
+                                                 int                   io_priority,
+                                                 GCancellable         *cancellable,
+                                                 GAsyncReadyCallback   callback,
+                                                 gpointer              data);
+static gboolean g_input_stream_real_close_finish (GInputStream         *stream,
+                                                 GAsyncResult         *result,
+                                                 GError              **error);
+
+static void
+g_input_stream_finalize (GObject *object)
+{
+  GInputStream *stream;
+
+  stream = G_INPUT_STREAM (object);
+  
+  if (!stream->priv->closed)
+    g_input_stream_close (stream, NULL, NULL);
+
+  if (G_OBJECT_CLASS (g_input_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_input_stream_dispose (GObject *object)
+{
+  GInputStream *stream;
+
+  stream = G_INPUT_STREAM (object);
+  
+  if (!stream->priv->closed)
+    g_input_stream_close (stream, NULL, NULL);
+  
+  if (G_OBJECT_CLASS (g_input_stream_parent_class)->dispose)
+    (*G_OBJECT_CLASS (g_input_stream_parent_class)->dispose) (object);
+}
+
+
+static void
+g_input_stream_class_init (GInputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GInputStreamPrivate));
+  
+  gobject_class->finalize = g_input_stream_finalize;
+  gobject_class->dispose = g_input_stream_dispose;
+  
+  klass->skip = g_input_stream_real_skip;
+  klass->read_async = g_input_stream_real_read_async;
+  klass->read_finish = g_input_stream_real_read_finish;
+  klass->skip_async = g_input_stream_real_skip_async;
+  klass->skip_finish = g_input_stream_real_skip_finish;
+  klass->close_async = g_input_stream_real_close_async;
+  klass->close_finish = g_input_stream_real_close_finish;
+}
+
+static void
+g_input_stream_init (GInputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                             G_TYPE_INPUT_STREAM,
+                                             GInputStreamPrivate);
+}
+
+/**
+ * g_input_stream_read:
+ * @stream: a #GInputStream.
+ * @buffer: a buffer to read data into (which should be at least count bytes long).
+ * @count: the number of bytes that will be read from the stream
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Tries to read @count bytes from the stream into the buffer starting at
+ * @buffer. Will block during this read.
+ * 
+ * If count is zero returns zero and does nothing. A value of @count
+ * larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
+ *
+ * On success, the number of bytes read into the buffer is returned.
+ * It is not an error if this is not the same as the requested size, as it
+ * can happen e.g. near the end of a file. Zero is returned on end of file
+ * (or if @count is zero),  but never otherwise.
+ *
+ * If @cancellable is not NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ * On error -1 is returned and @error is set accordingly.
+ * 
+ * Return value: Number of bytes read, or -1 on error
+ **/
+gssize
+g_input_stream_read  (GInputStream  *stream,
+                     void          *buffer,
+                     gsize          count,
+                     GCancellable  *cancellable,
+                     GError       **error)
+{
+  GInputStreamClass *class;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), -1);
+  g_return_val_if_fail (buffer != NULL, 0);
+
+  if (count == 0)
+    return 0;
+  
+  if (((gssize) count) < 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Too large count value passed to g_input_stream_read"));
+      return -1;
+    }
+
+  if (stream->priv->closed)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return -1;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return -1;
+    }
+  
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+
+  if (class->read == NULL) 
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Input stream doesn't implement read"));
+      return -1;
+    }
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  stream->priv->pending = TRUE;
+  res = class->read (stream, buffer, count, cancellable, error);
+  stream->priv->pending = FALSE;
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  return res;
+}
+
+/**
+ * g_input_stream_read_all:
+ * @stream: a #GInputStream.
+ * @buffer: a buffer to read data into (which should be at least count bytes long).
+ * @count: the number of bytes that will be read from the stream
+ * @bytes_read: location to store the number of bytes that was read from the stream
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Tries to read @count bytes from the stream into the buffer starting at
+ * @buffer. Will block during this read.
+ *
+ * This function is similar to g_input_stream_read(), except it tries to
+ * read as many bytes as requested, only stopping on an error or end of stream.
+ *
+ * On a successful read of @count bytes, or if we reached the end of the
+ * stream,  TRUE is returned, and @bytes_read is set to the number of bytes
+ * read into @buffer.
+ * 
+ * If there is an error during the operation FALSE is returned and @error
+ * is set to indicate the error status, @bytes_read is updated to contain
+ * the number of bytes read into @buffer before the error occured.
+ *
+ * Return value: TRUE on success, FALSE if there was an error
+ **/
+gboolean
+g_input_stream_read_all (GInputStream              *stream,
+                        void                      *buffer,
+                        gsize                      count,
+                        gsize                     *bytes_read,
+                        GCancellable              *cancellable,
+                        GError                   **error)
+{
+  gsize _bytes_read;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (buffer != NULL, FALSE);
+
+  _bytes_read = 0;
+  while (_bytes_read < count)
+    {
+      res = g_input_stream_read (stream, (char *)buffer + _bytes_read, count - _bytes_read,
+                                cancellable, error);
+      if (res == -1)
+       {
+         if (bytes_read)
+           *bytes_read = _bytes_read;
+         return FALSE;
+       }
+      
+      if (res == 0)
+       break;
+
+      _bytes_read += res;
+    }
+
+  if (bytes_read)
+    *bytes_read = _bytes_read;
+  return TRUE;
+}
+
+/**
+ * g_input_stream_skip:
+ * @stream: a #GInputStream.
+ * @count: the number of bytes that will be skipped from the stream
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Tries to skip @count bytes from the stream. Will block during the operation.
+ *
+ * This is identical to g_input_stream_read(), from a behaviour standpoint,
+ * but the bytes that are skipped are not returned to the user. Some
+ * streams have an implementation that is more efficient than reading the data.
+ *
+ * This function is optional for inherited classes, as the default implementation
+ * emulates it using read.
+ *
+ * If @cancellable is not %NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ * Return value: Number of bytes skipped, or -1 on error
+ **/
+gssize
+g_input_stream_skip (GInputStream         *stream,
+                    gsize                 count,
+                    GCancellable         *cancellable,
+                    GError              **error)
+{
+  GInputStreamClass *class;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), -1);
+
+  if (count == 0)
+    return 0;
+
+  if (((gssize) count) < 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Too large count value passed to g_input_stream_skip"));
+      return -1;
+    }
+  
+  if (stream->priv->closed)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return -1;
+    }
+
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return -1;
+    }
+  
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  stream->priv->pending = TRUE;
+  res = class->skip (stream, count, cancellable, error);
+  stream->priv->pending = FALSE;
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  return res;
+}
+
+static gssize
+g_input_stream_real_skip (GInputStream         *stream,
+                         gsize                 count,
+                         GCancellable         *cancellable,
+                         GError              **error)
+{
+  GInputStreamClass *class;
+  gssize ret, read_bytes;
+  char buffer[8192];
+  GError *my_error;
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+
+  if (G_IS_SEEKABLE (stream) && g_seekable_can_seek (G_SEEKABLE (stream)))
+    {
+      if (g_seekable_seek (G_SEEKABLE (stream),
+                          count,
+                          G_SEEK_CUR,
+                          cancellable,
+                          NULL))
+       return count;
+    }
+
+  /* If not seekable, or seek failed, fall back to reading data: */
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  
+  read_bytes = 0;
+  while (1)
+    {
+      my_error = NULL;
+
+      ret = class->read (stream, buffer, MIN (sizeof (buffer), count),
+                        cancellable, &my_error);
+      if (ret == -1)
+       {
+         if (read_bytes > 0 &&
+             my_error->domain == G_IO_ERROR &&
+             my_error->code == G_IO_ERROR_CANCELLED)
+           {
+             g_error_free (my_error);
+             return read_bytes;
+           }
+         
+         g_propagate_error (error, my_error);
+         return -1;
+       }
+
+      count -= ret;
+      read_bytes += ret;
+      
+      if (ret == 0 || count == 0)
+       return read_bytes;
+    }
+}
+
+/**
+ * g_input_stream_close:
+ * @stream: A #GInputStream.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Closes the stream, releasing resources related to it.
+ *
+ * Once the stream is closed, all other operations will return %G_IO_ERROR_CLOSED.
+ * Closing a stream multiple times will not return an error.
+ *
+ * Streams will be automatically closed when the last reference
+ * is dropped, but you might want to call make sure resources
+ * are released as early as possible.
+ *
+ * Some streams might keep the backing store of the stream (e.g. a file descriptor)
+ * open after the stream is closed. See the documentation for the individual
+ * stream for details.
+ *
+ * On failure the first error that happened will be reported, but the close
+ * operation will finish as much as possible. A stream that failed to
+ * close will still return %G_IO_ERROR_CLOSED all operations. Still, it
+ * is important to check and report the error to the user.
+ *
+ * If @cancellable is not NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ * Cancelling a close will still leave the stream closed, but some streams
+ * can use a faster close that doesn't block to e.g. check errors. 
+ *
+ * Return value: %TRUE on success, %FALSE on failure
+ **/
+gboolean
+g_input_stream_close (GInputStream  *stream,
+                     GCancellable  *cancellable,
+                     GError       **error)
+{
+  GInputStreamClass *class;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+
+  if (stream->priv->closed)
+    return TRUE;
+
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return FALSE;
+    }
+  
+  res = TRUE;
+
+  stream->priv->pending = TRUE;
+
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+
+  if (class->close)
+    res = class->close (stream, cancellable, error);
+
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  stream->priv->closed = TRUE;
+  
+  stream->priv->pending = FALSE;
+
+  return res;
+}
+
+static void
+async_ready_callback_wrapper (GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer      user_data)
+{
+  GInputStream *stream = G_INPUT_STREAM (source_object);
+
+  stream->priv->pending = FALSE;
+  if (stream->priv->outstanding_callback)
+    (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+static void
+async_ready_close_callback_wrapper (GObject *source_object,
+                                   GAsyncResult *res,
+                                   gpointer      user_data)
+{
+  GInputStream *stream = G_INPUT_STREAM (source_object);
+
+  stream->priv->pending = FALSE;
+  stream->priv->closed = TRUE;
+  if (stream->priv->outstanding_callback)
+    (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+/**
+ * g_input_stream_read_async:
+ * @stream: A #GInputStream.
+ * @buffer: a buffer to read data into (which should be at least count bytes long).
+ * @count: the number of bytes that will be read from the stream
+ * @io_priority: the io priority of the request. the io priority of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Request an asynchronous read of @count bytes from the stream into the buffer
+ * starting at @buffer. When the operation is finished @callback will be called,
+ * giving the results.
+ *
+ * During an async request no other sync and async calls are allowed, and will
+ * result in %G_IO_ERROR_PENDING errors. 
+ *
+ * A value of @count larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
+ *
+ * On success, the number of bytes read into the buffer will be passed to the
+ * callback. It is not an error if this is not the same as the requested size, as it
+ * can happen e.g. near the end of a file, but generally we try to read
+ * as many bytes as requested. Zero is returned on end of file
+ * (or if @count is zero),  but never otherwise.
+ *
+ * Any outstanding i/o request with higher priority (lower numerical value) will
+ * be executed before an outstanding request with lower priority. Default
+ * priority is %G_PRIORITY_DEFAULT.
+ *
+ * The asyncronous methods have a default fallback that uses threads to implement
+ * asynchronicity, so they are optional for inheriting classes. However, if you
+ * override one you must override all.
+ **/
+void
+g_input_stream_read_async (GInputStream        *stream,
+                          void                *buffer,
+                          gsize                count,
+                          int                  io_priority,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  GInputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_INPUT_STREAM (stream));
+  g_return_if_fail (buffer != NULL);
+
+  if (count == 0)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_input_stream_read_async);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+  
+  if (((gssize) count) < 0)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                          _("Too large count value passed to g_input_stream_read_async"));
+      return;
+    }
+
+  if (stream->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  
+  stream->priv->pending = TRUE;
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->read_async (stream, buffer, count, io_priority, cancellable,
+                    async_ready_callback_wrapper, user_data);
+}
+
+/**
+ * g_input_stream_read_finish:
+ * @stream:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+gssize
+g_input_stream_read_finish (GInputStream              *stream,
+                           GAsyncResult              *result,
+                           GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  GInputStreamClass *class;
+  
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), -1);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return -1;
+
+      /* Special case read of 0 bytes */
+      if (g_simple_async_result_get_source_tag (simple) == g_input_stream_read_async)
+       return 0;
+    }
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  return class->read_finish (stream, result, error);
+}
+
+/**
+ * g_input_stream_skip_async:
+ * @stream: A #GInputStream.
+ * @count: the number of bytes that will be skipped from the stream
+ * @io_priority: the io priority of the request. the io priority of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Request an asynchronous skip of @count bytes from the stream into the buffer
+ * starting at @buffer. When the operation is finished @callback will be called,
+ * giving the results.
+ *
+ * During an async request no other sync and async calls are allowed, and will
+ * result in %G_IO_ERROR_PENDING errors. 
+ *
+ * A value of @count larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
+ *
+ * On success, the number of bytes skipped will be passed to the
+ * callback. It is not an error if this is not the same as the requested size, as it
+ * can happen e.g. near the end of a file, but generally we try to skip
+ * as many bytes as requested. Zero is returned on end of file
+ * (or if @count is zero), but never otherwise.
+ *
+ * Any outstanding i/o request with higher priority (lower numerical value) will
+ * be executed before an outstanding request with lower priority. Default
+ * priority is %G_PRIORITY_DEFAULT.
+ *
+ * The asyncronous methods have a default fallback that uses threads to implement
+ * asynchronicity, so they are optional for inheriting classes. However, if you
+ * override one you must override all.
+ **/
+void
+g_input_stream_skip_async (GInputStream        *stream,
+                          gsize                count,
+                          int                  io_priority,
+                          GCancellable        *cancellable,
+                          GAsyncReadyCallback  callback,
+                          gpointer             user_data)
+{
+  GInputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_INPUT_STREAM (stream));
+
+  if (count == 0)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_input_stream_skip_async);
+
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+  
+  if (((gssize) count) < 0)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                          _("Too large count value passed to g_input_stream_skip_async"));
+      return;
+    }
+
+  if (stream->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  stream->priv->pending = TRUE;
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->skip_async (stream, count, io_priority, cancellable,
+                    async_ready_callback_wrapper, user_data);
+}
+
+/**
+ * g_input_stream_skip_finish:
+ * @stream:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+gssize
+g_input_stream_skip_finish (GInputStream              *stream,
+                           GAsyncResult              *result,
+                           GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  GInputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), -1);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return -1;
+
+      /* Special case skip of 0 bytes */
+      if (g_simple_async_result_get_source_tag (simple) == g_input_stream_skip_async)
+       return 0;
+    }
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  return class->skip_finish (stream, result, error);
+}
+
+/**
+ * g_input_stream_close_async:
+ * @stream: A #GInputStream.
+ * @io_priority: the io priority of the request. the io priority of the request
+ * @cancellable: optional cancellable object
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Requests an asynchronous closes of the stream, releasing resources related to it.
+ * When the operation is finished @callback will be called, giving the results.
+ *
+ * For behaviour details see g_input_stream_close().
+ *
+ * The asyncronous methods have a default fallback that uses threads to implement
+ * asynchronicity, so they are optional for inheriting classes. However, if you
+ * override one you must override all.
+ **/
+void
+g_input_stream_close_async (GInputStream       *stream,
+                           int                 io_priority,
+                           GCancellable       *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer            user_data)
+{
+  GInputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_INPUT_STREAM (stream));
+
+  if (stream->priv->closed)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_input_stream_close_async);
+
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+  
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  stream->priv->pending = TRUE;
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->close_async (stream, io_priority, cancellable,
+                     async_ready_close_callback_wrapper, user_data);
+}
+
+/**
+ * g_input_stream_close_finish:
+ * @stream:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+gboolean
+g_input_stream_close_finish (GInputStream              *stream,
+                            GAsyncResult              *result,
+                            GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  GInputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+
+      /* Special case already closed */
+      if (g_simple_async_result_get_source_tag (simple) == g_input_stream_close_async)
+       return TRUE;
+    }
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+  return class->close_finish (stream, result, error);
+}
+
+/**
+ * g_input_stream_is_closed:
+ * @stream: input stream.
+ * 
+ * Returns: %TRUE if the stream is closed.
+ **/
+gboolean
+g_input_stream_is_closed (GInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), TRUE);
+  
+  return stream->priv->closed;
+}
+/**
+ * g_input_stream_has_pending:
+ * @stream: input stream.
+ * 
+ * Returns: %TRUE if @stream has pending actions.
+ **/  
+gboolean
+g_input_stream_has_pending (GInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_INPUT_STREAM (stream), TRUE);
+  
+  return stream->priv->pending;
+}
+
+/**
+ * g_input_stream_set_pending:
+ * @stream: input stream
+ * @pending: boolean.
+ * 
+ * Sets @stream has actions pending.
+ **/
+void
+g_input_stream_set_pending (GInputStream              *stream,
+                           gboolean                   pending)
+{
+  g_return_if_fail (G_IS_INPUT_STREAM (stream));
+  
+  stream->priv->pending = pending;
+}
+
+/********************************************
+ *   Default implementation of async ops    *
+ ********************************************/
+
+typedef struct {
+  void              *buffer;
+  gsize              count_requested;
+  gssize             count_read;
+} ReadData;
+
+static void
+read_async_thread (GSimpleAsyncResult *res,
+                  GObject *object,
+                  GCancellable *cancellable)
+{
+  ReadData *op;
+  GInputStreamClass *class;
+  GError *error = NULL;
+  op = g_simple_async_result_get_op_res_gpointer (res);
+
+  class = G_INPUT_STREAM_GET_CLASS (object);
+
+  op->count_read = class->read (G_INPUT_STREAM (object),
+                               op->buffer, op->count_requested,
+                               cancellable, &error);
+  if (op->count_read == -1)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+static void
+g_input_stream_real_read_async (GInputStream *stream,
+                               void *buffer,
+                               gsize count,
+                               int io_priority,
+                               GCancellable *cancellable,
+                               GAsyncReadyCallback callback,
+                               gpointer user_data)
+{
+  GSimpleAsyncResult *res;
+  ReadData *op;
+  
+  op = g_new (ReadData, 1);
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_input_stream_real_read_async);
+  g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+  op->buffer = buffer;
+  op->count_requested = count;
+  
+  g_simple_async_result_run_in_thread (res, read_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static gssize
+g_input_stream_real_read_finish (GInputStream              *stream,
+                                GAsyncResult              *result,
+                                GError                   **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  ReadData *op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+           g_input_stream_real_read_async);
+
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+
+  return op->count_read;
+}
+
+typedef struct {
+  gsize count_requested;
+  gssize count_skipped;
+} SkipData;
+
+
+static void
+skip_async_thread (GSimpleAsyncResult *res,
+                  GObject *object,
+                  GCancellable *cancellable)
+{
+  SkipData *op;
+  GInputStreamClass *class;
+  GError *error = NULL;
+  
+  class = G_INPUT_STREAM_GET_CLASS (object);
+  op = g_simple_async_result_get_op_res_gpointer (res);
+  op->count_skipped = class->skip (G_INPUT_STREAM (object),
+                                  op->count_requested,
+                                  cancellable, &error);
+  if (op->count_skipped == -1)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+typedef struct {
+  char buffer[8192];
+  gsize count;
+  gsize count_skipped;
+  int io_prio;
+  GCancellable *cancellable;
+  gpointer user_data;
+  GAsyncReadyCallback callback;
+} SkipFallbackAsyncData;
+
+static void
+skip_callback_wrapper (GObject *source_object,
+                      GAsyncResult *res,
+                      gpointer user_data)
+{
+  GInputStreamClass *class;
+  SkipFallbackAsyncData *data = user_data;
+  SkipData *op;
+  GSimpleAsyncResult *simple;
+  GError *error = NULL;
+  gssize ret;
+
+  ret = g_input_stream_read_finish (G_INPUT_STREAM (source_object), res, &error);
+
+  if (ret > 0)
+    {
+      data->count -= ret;
+      data->count_skipped += ret;
+
+      if (data->count > 0)
+       {
+         class = G_INPUT_STREAM_GET_CLASS (source_object);
+         class->read_async (G_INPUT_STREAM (source_object), data->buffer, MIN (8192, data->count), data->io_prio, data->cancellable,
+                            skip_callback_wrapper, data);
+         return;
+       }
+    }
+
+  op = g_new0 (SkipData, 1);
+  op->count_skipped = data->count_skipped;
+  simple = g_simple_async_result_new (source_object,
+                                     data->callback, data->user_data,
+                                     g_input_stream_real_skip_async);
+
+  g_simple_async_result_set_op_res_gpointer (simple, op, g_free);
+
+  if (ret == -1)
+    {
+      if (data->count_skipped && 
+         error->domain == G_IO_ERROR &&
+         error->code == G_IO_ERROR_CANCELLED)
+       { /* No error, return partial read */ }
+      else
+       g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+  
+  g_free (data);
+ }
+
+static void
+g_input_stream_real_skip_async (GInputStream        *stream,
+                               gsize                count,
+                               int                  io_priority,
+                               GCancellable        *cancellable,
+                               GAsyncReadyCallback  callback,
+                               gpointer             user_data)
+{
+  GInputStreamClass *class;
+  SkipData *op;
+  SkipFallbackAsyncData *data;
+  GSimpleAsyncResult *res;
+
+  class = G_INPUT_STREAM_GET_CLASS (stream);
+
+  if (class->read_async == g_input_stream_real_read_async)
+    {
+      /* Read is thread-using async fallback.
+       * Make skip use threads too, so that we can use a possible sync skip
+       * implementation. */
+      op = g_new0 (SkipData, 1);
+      
+      res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data,
+                                      g_input_stream_real_skip_async);
+
+      g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+
+      op->count_requested = count;
+
+      g_simple_async_result_run_in_thread (res, skip_async_thread, io_priority, cancellable);
+      g_object_unref (res);
+    }
+  else
+    {
+      /* TODO: Skip fallback uses too much memory, should do multiple read calls */
+      
+      /* There is a custom async read function, lets use that. */
+      data = g_new (SkipFallbackAsyncData, 1);
+      data->count = count;
+      data->count_skipped = 0;
+      data->io_prio = io_priority;
+      data->cancellable = cancellable;
+      data->callback = callback;
+      data->user_data = user_data;
+      class->read_async (stream, data->buffer, MIN (8192, count), io_priority, cancellable,
+                        skip_callback_wrapper, data);
+    }
+
+}
+
+static gssize
+g_input_stream_real_skip_finish (GInputStream              *stream,
+                                GAsyncResult              *result,
+                                GError                   **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  SkipData *op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_input_stream_real_skip_async);
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  return op->count_skipped;
+}
+
+static void
+close_async_thread (GSimpleAsyncResult *res,
+                   GObject *object,
+                   GCancellable *cancellable)
+{
+  GInputStreamClass *class;
+  GError *error = NULL;
+  gboolean result;
+
+  /* Auto handling of cancelation disabled, and ignore
+     cancellation, since we want to close things anyway, although
+     possibly in a quick-n-dirty way. At least we never want to leak
+     open handles */
+  
+  class = G_INPUT_STREAM_GET_CLASS (object);
+  result = class->close (G_INPUT_STREAM (object), cancellable, &error);
+  if (!result)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+static void
+g_input_stream_real_close_async (GInputStream        *stream,
+                                int                  io_priority,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  GSimpleAsyncResult *res;
+  
+  res = g_simple_async_result_new (G_OBJECT (stream),
+                                  callback,
+                                  user_data,
+                                  g_input_stream_real_close_async);
+
+  g_simple_async_result_set_handle_cancellation (res, FALSE);
+  
+  g_simple_async_result_run_in_thread (res,
+                                      close_async_thread,
+                                      io_priority,
+                                      cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_input_stream_real_close_finish (GInputStream              *stream,
+                                 GAsyncResult              *result,
+                                 GError                   **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_input_stream_real_close_async);
+  return TRUE;
+}
diff --git a/gio/ginputstream.h b/gio/ginputstream.h
new file mode 100644 (file)
index 0000000..7df2662
--- /dev/null
@@ -0,0 +1,165 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_INPUT_STREAM_H__
+#define __G_INPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gioerror.h>
+#include <gio/gcancellable.h>
+#include <gio/gasyncresult.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_INPUT_STREAM         (g_input_stream_get_type ())
+#define G_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_INPUT_STREAM, GInputStream))
+#define G_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_INPUT_STREAM, GInputStreamClass))
+#define G_IS_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_INPUT_STREAM))
+#define G_IS_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_INPUT_STREAM))
+#define G_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_INPUT_STREAM, GInputStreamClass))
+
+typedef struct _GInputStream         GInputStream;
+typedef struct _GInputStreamClass    GInputStreamClass;
+typedef struct _GInputStreamPrivate  GInputStreamPrivate;
+
+struct _GInputStream
+{
+  GObject parent;
+
+  /*< private >*/
+  GInputStreamPrivate *priv;
+};
+
+struct _GInputStreamClass
+{
+  GObjectClass parent_class;
+
+  /* Sync ops: */
+  
+  gssize   (* read)        (GInputStream *stream,
+                           void         *buffer,
+                           gsize         count,
+                           GCancellable *cancellable,
+                           GError      **error);
+  gssize   (* skip)        (GInputStream *stream,
+                           gsize         count,
+                           GCancellable *cancellable,
+                           GError      **error);
+  gboolean (* close)      (GInputStream *stream,
+                           GCancellable *cancellable,
+                           GError      **error);
+
+  /* Async ops: (optional in derived classes) */
+  void     (* read_async)  (GInputStream        *stream,
+                           void               *buffer,
+                           gsize               count,
+                           int                 io_priority,
+                           GCancellable       *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer            user_data);
+  gssize   (* read_finish) (GInputStream       *stream,
+                           GAsyncResult       *result,
+                           GError            **error);
+  void     (* skip_async)  (GInputStream       *stream,
+                           gsize               count,
+                           int                 io_priority,
+                           GCancellable       *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer            user_data);
+  gssize   (* skip_finish) (GInputStream        *stream,
+                           GAsyncResult       *result,
+                           GError            **error);
+  void     (* close_async) (GInputStream        *stream,
+                           int                  io_priority,
+                           GCancellable       *cancellable,
+                           GAsyncReadyCallback callback,
+                           gpointer            user_data);
+  gboolean (* close_finish)(GInputStream        *stream,
+                           GAsyncResult       *result,
+                           GError            **error);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_input_stream_get_type (void) G_GNUC_CONST;
+
+gssize   g_input_stream_read         (GInputStream          *stream,
+                                     void                  *buffer,
+                                     gsize                  count,
+                                     GCancellable          *cancellable,
+                                     GError               **error);
+gboolean g_input_stream_read_all     (GInputStream          *stream,
+                                     void                  *buffer,
+                                     gsize                  count,
+                                     gsize                 *bytes_read,
+                                     GCancellable          *cancellable,
+                                     GError               **error);
+gssize   g_input_stream_skip         (GInputStream          *stream,
+                                     gsize                  count,
+                                     GCancellable          *cancellable,
+                                     GError               **error);
+gboolean g_input_stream_close        (GInputStream          *stream,
+                                     GCancellable          *cancellable,
+                                     GError               **error);
+void     g_input_stream_read_async   (GInputStream          *stream,
+                                     void                  *buffer,
+                                     gsize                  count,
+                                     int                    io_priority,
+                                     GCancellable          *cancellable,
+                                     GAsyncReadyCallback    callback,
+                                     gpointer               user_data);
+gssize   g_input_stream_read_finish  (GInputStream          *stream,
+                                     GAsyncResult          *result,
+                                     GError               **error);
+void     g_input_stream_skip_async   (GInputStream          *stream,
+                                     gsize                  count,
+                                     int                    io_priority,
+                                     GCancellable          *cancellable,
+                                     GAsyncReadyCallback    callback,
+                                     gpointer               user_data);
+gssize   g_input_stream_skip_finish  (GInputStream          *stream,
+                                     GAsyncResult          *result,
+                                     GError               **error);
+void     g_input_stream_close_async  (GInputStream          *stream,
+                                     int                    io_priority,
+                                     GCancellable          *cancellable,
+                                     GAsyncReadyCallback    callback,
+                                     gpointer               user_data);
+gboolean g_input_stream_close_finish (GInputStream          *stream,
+                                     GAsyncResult          *result,
+                                     GError               **error);
+
+/* For implementations: */
+
+gboolean g_input_stream_is_closed    (GInputStream          *stream);
+gboolean g_input_stream_has_pending  (GInputStream          *stream);
+void     g_input_stream_set_pending  (GInputStream          *stream,
+                                     gboolean               pending);
+
+G_END_DECLS
+
+#endif /* __G_INPUT_STREAM_H__ */
diff --git a/gio/gio-marshal.list b/gio/gio-marshal.list
new file mode 100644 (file)
index 0000000..5716663
--- /dev/null
@@ -0,0 +1,4 @@
+BOOLEAN:STRING,STRING,STRING,INT
+BOOLEAN:STRING,POINTER
+VOID:BOOLEAN,POINTER
+VOID:OBJECT,OBJECT,INT
diff --git a/gio/gioerror.c b/gio/gioerror.c
new file mode 100644 (file)
index 0000000..9b1553d
--- /dev/null
@@ -0,0 +1,155 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <errno.h>
+#include "gioerror.h"
+
+/**
+ * g_io_error_quark:
+ *
+ * Return value: The quark used as %G_IO_ERROR
+ **/
+GQuark
+g_io_error_quark (void)
+{
+  return g_quark_from_static_string ("g-io-error-quark");
+}
+
+GIOErrorEnum
+g_io_error_from_errno (gint err_no)
+{
+  switch (err_no)
+    {
+#ifdef EEXIST
+    case EEXIST:
+      return G_IO_ERROR_EXISTS;
+      break;
+#endif
+
+#ifdef EISDIR
+    case EISDIR:
+      return G_IO_ERROR_IS_DIRECTORY;
+      break;
+#endif
+
+#ifdef EACCES
+    case EACCES:
+      return G_IO_ERROR_PERMISSION_DENIED;
+      break;
+#endif
+
+#ifdef ENAMETOOLONG
+    case ENAMETOOLONG:
+      return G_IO_ERROR_FILENAME_TOO_LONG;
+      break;
+#endif
+
+#ifdef ENOENT
+    case ENOENT:
+      return G_IO_ERROR_NOT_FOUND;
+      break;
+#endif
+
+#ifdef ENOTDIR
+    case ENOTDIR:
+      return G_IO_ERROR_NOT_DIRECTORY;
+      break;
+#endif
+
+#ifdef EROFS
+    case EROFS:
+      return G_IO_ERROR_READ_ONLY;
+      break;
+#endif
+
+#ifdef ELOOP
+    case ELOOP:
+      return G_IO_ERROR_TOO_MANY_LINKS;
+      break;
+#endif
+
+#ifdef ENOSPC
+    case ENOSPC:
+      return G_IO_ERROR_NO_SPACE;
+      break;
+#endif
+
+#ifdef ENOMEM
+    case ENOMEM:
+      return G_IO_ERROR_NO_SPACE;
+      break;
+#endif
+      
+#ifdef EINVAL
+    case EINVAL:
+      return G_IO_ERROR_INVALID_ARGUMENT;
+      break;
+#endif
+
+#ifdef EPERM
+    case EPERM:
+      return G_IO_ERROR_PERMISSION_DENIED;
+      break;
+#endif
+
+#ifdef ECANCELED
+    case ECANCELED:
+      return G_IO_ERROR_CANCELLED;
+      break;
+#endif
+
+#ifdef ENOTEMPTY
+    case ENOTEMPTY:
+      return G_IO_ERROR_NOT_EMPTY;
+      break;
+#endif
+
+#ifdef ENOTSUP
+    case ENOTSUP:
+      return G_IO_ERROR_NOT_SUPPORTED;
+      break;
+#endif
+
+#ifdef ETIMEDOUT
+    case ETIMEDOUT:
+      return G_IO_ERROR_TIMED_OUT;
+      break;
+#endif
+
+#ifdef EBUSY
+    case EBUSY:
+      return G_IO_ERROR_BUSY;
+      break;
+#endif
+
+#ifdef EWOULDBLOCK
+    case EWOULDBLOCK:
+      return G_IO_ERROR_WOULD_BLOCK;
+      break;
+#endif
+      
+    default:
+      return G_IO_ERROR_FAILED;
+      break;
+    }
+}
diff --git a/gio/gioerror.h b/gio/gioerror.h
new file mode 100644 (file)
index 0000000..c4606fc
--- /dev/null
@@ -0,0 +1,78 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_IO_ERROR_H__
+#define __G_IO_ERROR_H__
+
+#include <glib/gerror.h>
+
+G_BEGIN_DECLS
+
+GQuark          g_io_error_quark      (void);
+
+#define G_IO_ERROR g_io_error_quark()
+
+/* This enumeration conflicts with GIOError in giochannel.h. However,
+ * that is only used as a return value in some deprecated functions.
+ * So, we reuse the same prefix for the enumeration values, but call
+ * the actual enumeration (which is rarely used) GIOErrorEnum.
+ */
+
+typedef enum
+{
+  G_IO_ERROR_FAILED,
+  G_IO_ERROR_NOT_FOUND,
+  G_IO_ERROR_EXISTS,
+  G_IO_ERROR_IS_DIRECTORY,
+  G_IO_ERROR_NOT_DIRECTORY,
+  G_IO_ERROR_NOT_EMPTY,
+  G_IO_ERROR_NOT_REGULAR_FILE,
+  G_IO_ERROR_NOT_SYMBOLIC_LINK,
+  G_IO_ERROR_NOT_MOUNTABLE_FILE,
+  G_IO_ERROR_FILENAME_TOO_LONG,
+  G_IO_ERROR_INVALID_FILENAME,
+  G_IO_ERROR_TOO_MANY_LINKS,
+  G_IO_ERROR_NO_SPACE,
+  G_IO_ERROR_INVALID_ARGUMENT,
+  G_IO_ERROR_PERMISSION_DENIED,
+  G_IO_ERROR_NOT_SUPPORTED,
+  G_IO_ERROR_NOT_MOUNTED,
+  G_IO_ERROR_ALREADY_MOUNTED,
+  G_IO_ERROR_CLOSED,
+  G_IO_ERROR_CANCELLED,
+  G_IO_ERROR_PENDING,
+  G_IO_ERROR_READ_ONLY,
+  G_IO_ERROR_CANT_CREATE_BACKUP,
+  G_IO_ERROR_WRONG_ETAG,
+  G_IO_ERROR_TIMED_OUT,
+  G_IO_ERROR_WOULD_RECURSE,
+  G_IO_ERROR_BUSY,
+  G_IO_ERROR_WOULD_BLOCK,
+  G_IO_ERROR_HOST_NOT_FOUND,
+  G_IO_ERROR_WOULD_MERGE
+} GIOErrorEnum;
+
+GIOErrorEnum g_io_error_from_errno (gint err_no);
+
+G_END_DECLS
+
+#endif /* __G_IO_ERROR_H__ */
diff --git a/gio/giomodule.c b/gio/giomodule.c
new file mode 100644 (file)
index 0000000..ab7c29d
--- /dev/null
@@ -0,0 +1,237 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "giomodule.h"
+
+struct _GIOModule {
+  GTypeModule parent_instance;
+  
+  gchar       *filename;
+  GModule     *library;
+  
+  void (* load)   (GIOModule *module);
+  void (* unload) (GIOModule *module);
+};
+
+struct _GIOModuleClass
+{
+  GTypeModuleClass parent_class;
+
+};
+
+static void      g_io_module_finalize      (GObject      *object);
+static gboolean  g_io_module_load_module   (GTypeModule  *gmodule);
+static void      g_io_module_unload_module (GTypeModule  *gmodule);
+
+G_DEFINE_TYPE (GIOModule, g_io_module, G_TYPE_TYPE_MODULE);
+
+static void
+g_io_module_class_init (GIOModuleClass *class)
+{
+  GObjectClass     *object_class      = G_OBJECT_CLASS (class);
+  GTypeModuleClass *type_module_class = G_TYPE_MODULE_CLASS (class);
+
+  object_class->finalize     = g_io_module_finalize;
+
+  type_module_class->load    = g_io_module_load_module;
+  type_module_class->unload  = g_io_module_unload_module;
+}
+
+static void
+g_io_module_init (GIOModule *module)
+{
+}
+
+static void
+g_io_module_finalize (GObject *object)
+{
+  GIOModule *module = G_IO_MODULE (object);
+
+  g_free (module->filename);
+
+  G_OBJECT_CLASS (g_io_module_parent_class)->finalize (object);
+}
+
+static gboolean
+g_io_module_load_module (GTypeModule *gmodule)
+{
+  GIOModule *module = G_IO_MODULE (gmodule);
+
+  if (!module->filename)
+    {
+      g_warning ("GIOModule path not set");
+      return FALSE;
+    }
+
+  module->library = g_module_open (module->filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL);
+
+  if (!module->library)
+    {
+      g_printerr ("%s\n", g_module_error ());
+      return FALSE;
+    }
+
+  /* Make sure that the loaded library contains the required methods */
+  if (! g_module_symbol (module->library,
+                         "g_io_module_load",
+                         (gpointer *) &module->load) ||
+      ! g_module_symbol (module->library,
+                         "g_io_module_unload",
+                         (gpointer *) &module->unload))
+    {
+      g_printerr ("%s\n", g_module_error ());
+      g_module_close (module->library);
+
+      return FALSE;
+    }
+
+  /* Initialize the loaded module */
+  module->load (module);
+
+  return TRUE;
+}
+
+static void
+g_io_module_unload_module (GTypeModule *gmodule)
+{
+  GIOModule *module = G_IO_MODULE (gmodule);
+
+  module->unload (module);
+
+  g_module_close (module->library);
+  module->library = NULL;
+
+  module->load   = NULL;
+  module->unload = NULL;
+}
+
+/**
+ * g_io_module_new:
+ * @filename: filename of the module to load.
+ * 
+ * Returns: a new #GIOModule from given @filename, 
+ * or %NULL on error.
+ **/
+GIOModule *
+g_io_module_new (const gchar *filename)
+{
+  GIOModule *module;
+
+  g_return_val_if_fail (filename != NULL, NULL);
+
+  module = g_object_new (G_IO_TYPE_MODULE, NULL);
+  module->filename = g_strdup (filename);
+
+  return module;
+}
+
+static gboolean
+is_valid_module_name (const gchar *basename)
+{
+#if !defined(G_OS_WIN32) && !defined(G_WITH_CYGWIN)
+  return
+    g_str_has_prefix (basename, "lib") &&
+    g_str_has_suffix (basename, ".so");
+#else
+  return g_str_has_suffix (basename, ".dll");
+#endif
+}
+
+static GList *
+load_modules (const char *dirname)
+{
+  const gchar *name;
+  GDir        *dir;
+  GList *modules;
+
+  if (!g_module_supported ())
+    return NULL;
+
+  dir = g_dir_open (dirname, 0, NULL);
+  if (!dir)
+    return NULL;
+
+  modules = NULL;
+  while ((name = g_dir_read_name (dir)))
+    {
+      if (is_valid_module_name (name))
+        {
+          GIOModule *module;
+          gchar     *path;
+
+          path = g_build_filename (dirname, name, NULL);
+          module = g_io_module_new (path);
+
+          if (!g_type_module_use (G_TYPE_MODULE (module)))
+            {
+              g_printerr ("Failed to load module: %s\n", path);
+              g_object_unref (module);
+              g_free (path);
+              continue;
+            }
+         
+          g_free (path);
+
+          g_type_module_unuse (G_TYPE_MODULE (module));
+         
+          modules = g_list_prepend (modules, module);
+        }
+    }
+  
+  g_dir_close (dir);
+
+  return modules;
+}
+
+G_LOCK_DEFINE_STATIC (loaded_dirs);
+static GHashTable *loaded_dirs = NULL;
+
+/**
+ * g_io_module_ensure_loaded:
+ * @directory: directory to ensure is loaded.
+ * 
+ **/
+void
+g_io_modules_ensure_loaded (const char *directory)
+{
+  GList *modules;
+
+  g_return_if_fail (directory != NULL);
+  
+  G_LOCK (loaded_dirs);
+
+  if (loaded_dirs == NULL)
+    loaded_dirs = g_hash_table_new (g_str_hash, g_str_equal);
+
+  if (!g_hash_table_lookup_extended (loaded_dirs, directory,
+                                    NULL, NULL))
+    {
+      modules = load_modules (directory);
+      g_hash_table_insert (loaded_dirs,
+                          g_strdup (directory),
+                          modules);
+    }
+  
+  G_UNLOCK (loaded_dirs);
+}
diff --git a/gio/giomodule.h b/gio/giomodule.h
new file mode 100644 (file)
index 0000000..44ed052
--- /dev/null
@@ -0,0 +1,52 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_IO_MODULE_H__
+#define __G_IO_MODULE_H__
+
+#include <glib-object.h>
+#include <gmodule.h>
+
+G_BEGIN_DECLS
+
+#define G_IO_TYPE_MODULE         (g_io_module_get_type ())
+#define G_IO_MODULE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_IO_TYPE_MODULE, GIOModule))
+#define G_IO_MODULE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_IO_TYPE_MODULE, GIOModuleClass))
+#define G_IO_IS_MODULE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_IO_TYPE_MODULE))
+#define G_IO_IS_MODULE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_IO_TYPE_MODULE))
+#define G_IO_MODULE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_IO_TYPE_MODULE, GIOModuleClass))
+
+typedef struct _GIOModule GIOModule;
+typedef struct _GIOModuleClass GIOModuleClass;
+
+GType      g_io_module_get_type (void) G_GNUC_CONST;
+GIOModule *g_io_module_new      (const gchar *filename);
+
+void       g_io_modules_ensure_loaded (const char *directory);
+
+/* API for the modules to implement */
+void        g_io_module_load     (GIOModule   *module);
+void        g_io_module_unload   (GIOModule   *module);
+
+G_END_DECLS
+
+#endif /* __G_IO_MODULE_H__ */
diff --git a/gio/gioscheduler.c b/gio/gioscheduler.c
new file mode 100644 (file)
index 0000000..1c5f548
--- /dev/null
@@ -0,0 +1,372 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "gioscheduler.h"
+
+struct _GIOJob {
+  GSList *active_link;
+  GIOJobFunc job_func;
+  GIODataFunc cancel_func; /* Runs under job map lock */
+  gpointer data;
+  GDestroyNotify destroy_notify;
+
+  gint io_priority;
+  GCancellable *cancellable;
+
+  guint idle_tag;
+};
+
+G_LOCK_DEFINE_STATIC(active_jobs);
+static GSList *active_jobs = NULL;
+
+static GThreadPool *job_thread_pool = NULL;
+
+static void io_job_thread (gpointer       data,
+                          gpointer       user_data);
+
+static void
+g_io_job_free (GIOJob *job)
+{
+  if (job->cancellable)
+    g_object_unref (job->cancellable);
+  g_free (job);
+}
+
+static gint
+g_io_job_compare (gconstpointer  a,
+                 gconstpointer  b,
+                 gpointer       user_data)
+{
+  const GIOJob *aa = a;
+  const GIOJob *bb = b;
+
+  /* Cancelled jobs are set prio == -1, so that
+     they are executed as quickly as possible */
+  
+  /* Lower value => higher priority */
+  if (aa->io_priority < bb->io_priority)
+    return -1;
+  if (aa->io_priority == bb->io_priority)
+    return 0;
+  return 1;
+}
+
+static gpointer
+init_scheduler (gpointer arg)
+{
+  if (job_thread_pool == NULL)
+    {
+      /* TODO: thread_pool_new can fail */
+      job_thread_pool = g_thread_pool_new (io_job_thread,
+                                          NULL,
+                                          10,
+                                          FALSE,
+                                          NULL);
+      if (job_thread_pool != NULL)
+       {
+         g_thread_pool_set_sort_function (job_thread_pool,
+                                          g_io_job_compare,
+                                          NULL);
+         /* Its kinda weird that this is a global setting
+          * instead of per threadpool. However, we really
+          * want to cache some threads, but not keep around
+          * those threads forever. */
+         g_thread_pool_set_max_idle_time (15 * 1000);
+         g_thread_pool_set_max_unused_threads (2);
+       }
+    }
+  return NULL;
+}
+
+static void
+remove_active_job (GIOJob *job)
+{
+  GIOJob *other_job;
+  GSList *l;
+  gboolean resort_jobs;
+  
+  G_LOCK (active_jobs);
+  active_jobs = g_slist_delete_link (active_jobs, job->active_link);
+  
+  resort_jobs = FALSE;
+  for (l = active_jobs; l != NULL; l = l->next)
+    {
+      other_job = l->data;
+      if (other_job->io_priority >= 0 &&
+         g_cancellable_is_cancelled (other_job->cancellable))
+       {
+         other_job->io_priority = -1;
+         resort_jobs = TRUE;
+       }
+    }
+  G_UNLOCK (active_jobs);
+  
+  if (resort_jobs &&
+      job_thread_pool != NULL)
+    g_thread_pool_set_sort_function (job_thread_pool,
+                                    g_io_job_compare,
+                                    NULL);
+
+}
+
+static void
+io_job_thread (gpointer       data,
+              gpointer       user_data)
+{
+  GIOJob *job = data;
+
+  if (job->cancellable)
+    g_push_current_cancellable (job->cancellable);
+  job->job_func (job, job->cancellable, job->data);
+  if (job->cancellable)
+    g_pop_current_cancellable (job->cancellable);
+
+  if (job->destroy_notify)
+    job->destroy_notify (job->data);
+
+  remove_active_job (job);
+  g_io_job_free (job);
+
+}
+
+static gboolean
+run_job_at_idle (gpointer data)
+{
+  GIOJob *job = data;
+
+  if (job->cancellable)
+    g_push_current_cancellable (job->cancellable);
+  
+  job->job_func (job, job->cancellable, job->data);
+  
+  if (job->cancellable)
+    g_pop_current_cancellable (job->cancellable);
+
+  if (job->destroy_notify)
+    job->destroy_notify (job->data);
+
+  remove_active_job (job);
+  g_io_job_free (job);
+
+  return FALSE;
+}
+
+/**
+ * g_schedule_io_job:
+ * @job_func: a #GIOJobFunc.
+ * @user_data: a #gpointer.
+ * @notify: a #GDestroyNotify.
+ * @io_priority: the io priority of the request. a #gint.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ *
+ * Schedules the @job_func.
+ * 
+ **/
+void
+g_schedule_io_job (GIOJobFunc     job_func,
+                  gpointer       user_data,
+                  GDestroyNotify notify,
+                  gint           io_priority,
+                  GCancellable  *cancellable)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  GIOJob *job;
+
+  g_return_if_fail (job_func != NULL);
+
+  job = g_new0 (GIOJob, 1);
+  job->job_func = job_func;
+  job->data = user_data;
+  job->destroy_notify = notify;
+  job->io_priority = io_priority;
+    
+  if (cancellable)
+    job->cancellable = g_object_ref (cancellable);
+
+  G_LOCK (active_jobs);
+  active_jobs = g_slist_prepend (active_jobs, job);
+  job->active_link = active_jobs;
+  G_UNLOCK (active_jobs);
+
+  if (g_thread_supported())
+    {
+      g_once (&once_init, init_scheduler, NULL);
+      g_thread_pool_push (job_thread_pool, job, NULL);
+    }
+  else
+    {
+      /* Threads not available, instead do the i/o sync inside a
+       * low prio idle handler
+       */
+      job->idle_tag = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE + 1 + io_priority / 10,
+                                      run_job_at_idle,
+                                      job, NULL);
+    }
+}
+
+/**
+ * g_cancel_all_io_jobs:
+ * 
+ * Cancels all cancellable I/O Jobs. 
+ **/
+void
+g_cancel_all_io_jobs (void)
+{
+  GSList *cancellable_list, *l;
+  
+  G_LOCK (active_jobs);
+  cancellable_list = NULL;
+  for (l = active_jobs; l != NULL; l = l->next)
+    {
+      GIOJob *job = l->data;
+      if (job->cancellable)
+       cancellable_list = g_slist_prepend (cancellable_list,
+                                           g_object_ref (job->cancellable));
+    }
+  G_UNLOCK (active_jobs);
+
+  for (l = cancellable_list; l != NULL; l = l->next)
+    {
+      GCancellable *c = l->data;
+      g_cancellable_cancel (c);
+      g_object_unref (c);
+    }
+  g_slist_free (cancellable_list);
+}
+
+typedef struct {
+  GIODataFunc func;
+  gpointer    data;
+  GDestroyNotify notify;
+
+  GMutex *ack_lock;
+  GCond *ack_condition;
+} MainLoopProxy;
+
+static gboolean
+mainloop_proxy_func (gpointer data)
+{
+  MainLoopProxy *proxy = data;
+
+  proxy->func (proxy->data);
+
+  if (proxy->ack_lock)
+    {
+      g_mutex_lock (proxy->ack_lock);
+      g_cond_signal (proxy->ack_condition);
+      g_mutex_unlock (proxy->ack_lock);
+    }
+  
+  return FALSE;
+}
+
+static void
+mainloop_proxy_free (MainLoopProxy *proxy)
+{
+  if (proxy->ack_lock)
+    {
+      g_mutex_free (proxy->ack_lock);
+      g_cond_free (proxy->ack_condition);
+    }
+  
+  g_free (proxy);
+}
+
+static void
+mainloop_proxy_notify (gpointer data)
+{
+  MainLoopProxy *proxy = data;
+
+  if (proxy->notify)
+    proxy->notify (proxy->data);
+
+  /* If nonblocking we free here, otherwise we free in io thread */
+  if (proxy->ack_lock == NULL)
+    mainloop_proxy_free (proxy);
+}
+
+/**
+ * g_io_job_send_to_mainloop:
+ * @job: a #GIOJob.
+ * @func: a #GIODataFunc.
+ * @user_data: a #gpointer.
+ * @notify: a #GDestroyNotify.
+ * @block: boolean flag indicating whether or not this job should block.
+ * 
+ * 
+ **/
+void
+g_io_job_send_to_mainloop (GIOJob        *job,
+                          GIODataFunc    func,
+                          gpointer       user_data,
+                          GDestroyNotify notify,
+                          gboolean       block)
+{
+  GSource *source;
+  MainLoopProxy *proxy;
+  guint id;
+
+  g_return_if_fail (job != NULL);
+  g_return_if_fail (func != NULL);
+
+  if (job->idle_tag)
+    {
+      /* We just immediately re-enter in the case of idles (non-threads)
+       * Anything else would just deadlock. If you can't handle this, enable threads.
+       */
+      func (user_data); 
+      return;
+    }
+  
+  proxy = g_new0 (MainLoopProxy, 1);
+  proxy->func = func;
+  proxy->data = user_data;
+  proxy->notify = notify;
+  
+  if (block)
+    {
+      proxy->ack_lock = g_mutex_new ();
+      proxy->ack_condition = g_cond_new ();
+    }
+  
+  source = g_idle_source_new ();
+  g_source_set_priority (source, G_PRIORITY_DEFAULT);
+
+  g_source_set_callback (source, mainloop_proxy_func, proxy, mainloop_proxy_notify);
+
+  if (block)
+    g_mutex_lock (proxy->ack_lock);
+                 
+  id = g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  if (block)
+    {
+      g_cond_wait (proxy->ack_condition, proxy->ack_lock);
+      g_mutex_unlock (proxy->ack_lock);
+      
+      /* destroy notify didn't free proxy */
+      mainloop_proxy_free (proxy);
+    }
+}
diff --git a/gio/gioscheduler.h b/gio/gioscheduler.h
new file mode 100644 (file)
index 0000000..73f2684
--- /dev/null
@@ -0,0 +1,55 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_IO_SCHEDULER_H__
+#define __G_IO_SCHEDULER_H__
+
+#include <glib.h>
+#include <gio/gcancellable.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GIOJob GIOJob;
+
+typedef void (*GIOJobFunc) (GIOJob *job,
+                           GCancellable *cancellable,
+                           gpointer user_data);
+
+typedef void (*GIODataFunc) (gpointer user_data);
+
+void g_schedule_io_job         (GIOJobFunc      job_func,
+                               gpointer        user_data,
+                               GDestroyNotify  notify,
+                               gint            io_priority,
+                               GCancellable   *cancellable);
+void g_cancel_all_io_jobs      (void);
+
+void g_io_job_send_to_mainloop (GIOJob         *job,
+                               GIODataFunc     func,
+                               gpointer        user_data,
+                               GDestroyNotify  notify,
+                               gboolean        block);
+
+
+G_END_DECLS
+
+#endif /* __G_IO_SCHEDULER_H__ */
diff --git a/gio/gloadableicon.c b/gio/gloadableicon.c
new file mode 100644 (file)
index 0000000..569065e
--- /dev/null
@@ -0,0 +1,260 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "gsimpleasyncresult.h"
+#include "gloadableicon.h"
+#include "glibintl.h"
+
+static void          g_loadable_icon_real_load_async  (GLoadableIcon        *icon,
+                                                      int                   size,
+                                                      GCancellable         *cancellable,
+                                                      GAsyncReadyCallback   callback,
+                                                      gpointer              user_data);
+static GInputStream *g_loadable_icon_real_load_finish (GLoadableIcon        *icon,
+                                                      GAsyncResult         *res,
+                                                      char                **type,
+                                                      GError              **error);
+static void          g_loadable_icon_base_init        (gpointer              g_class);
+static void          g_loadable_icon_class_init       (gpointer              g_class,
+                                                      gpointer              class_data);
+
+GType
+g_loadable_icon_get_type (void)
+{
+  static GType loadable_icon_type = 0;
+
+  if (! loadable_icon_type)
+    {
+      static const GTypeInfo loadable_icon_info =
+       {
+        sizeof (GLoadableIconIface), /* class_size */
+       g_loadable_icon_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       g_loadable_icon_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      loadable_icon_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GLoadableIcon"),
+                               &loadable_icon_info, 0);
+
+      g_type_interface_add_prerequisite (loadable_icon_type, G_TYPE_ICON);
+    }
+
+  return loadable_icon_type;
+}
+
+static void
+g_loadable_icon_class_init (gpointer g_class,
+                           gpointer class_data)
+{
+  GLoadableIconIface *iface = g_class;
+
+  iface->load_async = g_loadable_icon_real_load_async;
+  iface->load_finish = g_loadable_icon_real_load_finish;
+}
+
+static void
+g_loadable_icon_base_init (gpointer g_class)
+{
+}
+
+/**
+ * g_loadable_icon_load:
+ * @icon:
+ * @size:
+ * @type:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+GInputStream *
+g_loadable_icon_load (GLoadableIcon        *icon,
+                     int                   size,
+                     char                **type,
+                     GCancellable         *cancellable,
+                     GError              **error)
+{
+  GLoadableIconIface *iface;
+
+  g_return_val_if_fail (G_IS_LOADABLE_ICON (icon), NULL);
+
+  iface = G_LOADABLE_ICON_GET_IFACE (icon);
+
+  return (* iface->load) (icon, size, type, cancellable, error);
+  
+}
+
+/**
+ * g_loadable_icon_load_async:
+ * @icon:
+ * @size:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. @callback:
+ * @user_data:
+ * 
+ * Loads an icon asynchronously.
+ * 
+ **/
+void
+g_loadable_icon_load_async (GLoadableIcon        *icon,
+                           int                   size,
+                           GCancellable         *cancellable,
+                           GAsyncReadyCallback   callback,
+                           gpointer              user_data)
+{
+  GLoadableIconIface *iface;
+  
+  g_return_if_fail (G_IS_LOADABLE_ICON (icon));
+
+  iface = G_LOADABLE_ICON_GET_IFACE (icon);
+
+  (* iface->load_async) (icon, size, cancellable, callback, user_data);
+  
+}
+
+/**
+ * g_loadable_icon_load_finish:
+ * @icon:
+ * @res:
+ * @type:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns:
+ **/
+GInputStream *
+g_loadable_icon_load_finish (GLoadableIcon        *icon,
+                            GAsyncResult         *res,
+                            char                **type,
+                            GError              **error)
+{
+  GLoadableIconIface *iface;
+  
+  g_return_val_if_fail (G_IS_LOADABLE_ICON (icon), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (res))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return NULL;
+    }
+  
+  iface = G_LOADABLE_ICON_GET_IFACE (icon);
+
+  return (* iface->load_finish) (icon, res, type, error);
+  
+}
+
+/********************************************
+ *   Default implementation of async load   *
+ ********************************************/
+
+typedef struct {
+  int size;
+  char *type;
+  GInputStream *stream;
+} LoadData;
+
+static void
+load_data_free (LoadData *data)
+{
+  if (data->stream)
+    g_object_unref (data->stream);
+  g_free (data->type);
+  g_free (data);
+}
+
+static void
+load_async_thread (GSimpleAsyncResult *res,
+                  GObject *object,
+                  GCancellable *cancellable)
+{
+  GLoadableIconIface *iface;
+  GInputStream *stream;
+  LoadData *data;
+  GError *error = NULL;
+  char *type = NULL;
+
+  data = g_simple_async_result_get_op_res_gpointer (res);
+  
+  iface = G_LOADABLE_ICON_GET_IFACE (object);
+  stream = iface->load (G_LOADABLE_ICON (object), data->size, &type, cancellable, &error);
+
+  if (stream == NULL)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+  else
+    {
+      data->stream = stream;
+      data->type = type;
+    }
+}
+
+
+
+static void
+g_loadable_icon_real_load_async (GLoadableIcon        *icon,
+                                int                   size,
+                                GCancellable         *cancellable,
+                                GAsyncReadyCallback   callback,
+                                gpointer              user_data)
+{
+  GSimpleAsyncResult *res;
+  LoadData *data;
+  
+  res = g_simple_async_result_new (G_OBJECT (icon), callback, user_data, g_loadable_icon_real_load_async);
+  data = g_new0 (LoadData, 1);
+  g_simple_async_result_set_op_res_gpointer (res, data, (GDestroyNotify) load_data_free);
+  g_simple_async_result_run_in_thread (res, load_async_thread, 0, cancellable);
+  g_object_unref (res);
+}
+
+static GInputStream *
+g_loadable_icon_real_load_finish (GLoadableIcon        *icon,
+                                 GAsyncResult         *res,
+                                 char                **type,
+                                 GError              **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  LoadData *data;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_loadable_icon_real_load_async);
+
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+
+  if (type)
+    {
+      *type = data->type;
+      data->type = NULL;
+    }
+
+  return g_object_ref (data->stream);
+}
diff --git a/gio/gloadableicon.h b/gio/gloadableicon.h
new file mode 100644 (file)
index 0000000..ef203d7
--- /dev/null
@@ -0,0 +1,83 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOADABLE_ICON_H__
+#define __G_LOADABLE_ICON_H__
+
+#include <glib-object.h>
+#include <gio/gicon.h>
+#include <gio/ginputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOADABLE_ICON            (g_loadable_icon_get_type ())
+#define G_LOADABLE_ICON(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_LOADABLE_ICON, GLoadableIcon))
+#define G_IS_LOADABLE_ICON(obj)                (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_LOADABLE_ICON))
+#define G_LOADABLE_ICON_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_LOADABLE_ICON, GLoadableIconIface))
+
+typedef struct _GLoadableIcon                  GLoadableIcon; /* Dummy typedef */
+typedef struct _GLoadableIconIface             GLoadableIconIface;
+
+
+struct _GLoadableIconIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+
+  GInputStream * (*load)        (GLoadableIcon      *icon,
+                                int                 size,
+                                char              **type,
+                                GCancellable       *cancellable,
+                                GError            **error);
+  void           (*load_async)  (GLoadableIcon      *icon,
+                                int                 size,
+                                GCancellable       *cancellable,
+                                GAsyncReadyCallback callback,
+                                gpointer            user_data);
+  GInputStream * (*load_finish) (GLoadableIcon      *icon,
+                                 GAsyncResult      *res,
+                                 char             **type,
+                                 GError           **error);
+};
+
+GType g_loadable_icon_get_type (void) G_GNUC_CONST;
+
+
+GInputStream *g_loadable_icon_load        (GLoadableIcon        *icon,
+                                          int                   size,
+                                          char                **type,
+                                          GCancellable         *cancellable,
+                                          GError              **error);
+void          g_loadable_icon_load_async  (GLoadableIcon        *icon,
+                                          int                   size,
+                                          GCancellable         *cancellable,
+                                          GAsyncReadyCallback   callback,
+                                          gpointer              user_data);
+GInputStream *g_loadable_icon_load_finish (GLoadableIcon        *icon,
+                                          GAsyncResult         *res,
+                                          char                **type,
+                                          GError              **error);
+
+G_END_DECLS
+
+#endif /* __G_LOADABLE_ICON_H__ */
diff --git a/gio/glocaldirectorymonitor.c b/gio/glocaldirectorymonitor.c
new file mode 100644 (file)
index 0000000..0cdcf8a
--- /dev/null
@@ -0,0 +1,290 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "glocaldirectorymonitor.h"
+#include "gunixmounts.h"
+#include "gdirectorymonitor.h"
+#include "giomodule.h"
+
+#include <string.h>
+
+enum
+{
+  PROP_0,
+  PROP_DIRNAME
+};
+
+static gboolean g_local_directory_monitor_cancel (GDirectoryMonitor* monitor);
+static void mounts_changed (GUnixMountMonitor *mount_monitor, gpointer user_data);
+
+G_DEFINE_ABSTRACT_TYPE (GLocalDirectoryMonitor, g_local_directory_monitor, G_TYPE_DIRECTORY_MONITOR)
+
+static void
+g_local_directory_monitor_finalize (GObject* object)
+{
+  GLocalDirectoryMonitor* local_monitor;
+  local_monitor = G_LOCAL_DIRECTORY_MONITOR (object);
+
+  g_free (local_monitor->dirname);
+
+  if (local_monitor->mount_monitor)
+    {
+      g_signal_handlers_disconnect_by_func (local_monitor->mount_monitor, mounts_changed, local_monitor);
+      g_object_unref (local_monitor->mount_monitor);
+      local_monitor->mount_monitor = NULL;
+    }
+
+  if (G_OBJECT_CLASS (g_local_directory_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_local_directory_monitor_parent_class)->finalize) (object);
+}
+
+static void
+g_local_directory_monitor_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+  switch (property_id)
+  {
+    case PROP_DIRNAME:
+      /* Do nothing */
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static GObject *
+g_local_directory_monitor_constructor (GType type,
+                                    guint n_construct_properties,
+                                    GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GLocalDirectoryMonitorClass *klass;
+  GObjectClass *parent_class;
+  GLocalDirectoryMonitor *local_monitor;
+  const gchar *dirname = NULL;
+  gint i;
+  
+  klass = G_LOCAL_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_LOCAL_DIRECTORY_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  local_monitor = G_LOCAL_DIRECTORY_MONITOR (obj);
+
+  for (i = 0; i < n_construct_properties; i++)
+    {
+      if (strcmp ("dirname", g_param_spec_get_name (construct_properties[i].pspec)) == 0)
+        {
+          g_assert (G_VALUE_HOLDS_STRING (construct_properties[i].value));
+          dirname = g_value_get_string (construct_properties[i].value);
+          break;
+        }
+    }
+
+  local_monitor->dirname = g_strdup (dirname);
+
+  if (!klass->mount_notify)
+    {
+      GUnixMount *mount;
+      
+      /* Emulate unmount detection */
+      
+      mount = g_get_unix_mount_at (local_monitor->dirname, NULL);
+      
+      local_monitor->was_mounted = mount != NULL;
+      
+      if (mount)
+        g_unix_mount_free (mount);
+
+      local_monitor->mount_monitor = g_unix_mount_monitor_new ();
+      g_signal_connect (local_monitor->mount_monitor, "mounts_changed",
+        G_CALLBACK (mounts_changed), local_monitor);
+    }
+
+  return obj;
+}
+
+static void
+g_local_directory_monitor_class_init (GLocalDirectoryMonitorClass* klass)
+{
+  GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+  GDirectoryMonitorClass *dir_monitor_class = G_DIRECTORY_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_local_directory_monitor_finalize;
+  gobject_class->set_property = g_local_directory_monitor_set_property;
+  gobject_class->constructor = g_local_directory_monitor_constructor;
+
+  dir_monitor_class->cancel = g_local_directory_monitor_cancel;
+
+  g_object_class_install_property (gobject_class, PROP_DIRNAME,
+    g_param_spec_string ("dirname", "Directory name", "Directory to monitor",
+        NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+
+  klass->mount_notify = FALSE;
+}
+
+static void
+g_local_directory_monitor_init (GLocalDirectoryMonitor* local_monitor)
+{
+}
+
+static void
+mounts_changed (GUnixMountMonitor *mount_monitor,
+                gpointer user_data)
+{
+  GLocalDirectoryMonitor *local_monitor = user_data;
+  GUnixMount *mount;
+  gboolean is_mounted;
+  GFile *file;
+  
+  /* Emulate unmount detection */
+  
+  mount = g_get_unix_mount_at (local_monitor->dirname, NULL);
+  
+  is_mounted = mount != NULL;
+  
+  if (mount)
+    g_unix_mount_free (mount);
+
+  if (local_monitor->was_mounted != is_mounted)
+    {
+      if (local_monitor->was_mounted && !is_mounted)
+        {
+          file = g_file_new_for_path (local_monitor->dirname);
+          g_directory_monitor_emit_event (G_DIRECTORY_MONITOR (local_monitor),
+                                          file, NULL,
+                                          G_FILE_MONITOR_EVENT_UNMOUNTED);
+          g_object_unref (file);
+        }
+      local_monitor->was_mounted = is_mounted;
+    }
+}
+
+static gint
+_compare_monitor_class_by_prio (gconstpointer a,
+                                gconstpointer b,
+                                gpointer user_data)
+{
+  GType *type1 = (GType *) a, *type2 = (GType *) b;
+  GLocalDirectoryMonitorClass *klass1, *klass2;
+  gint ret;
+
+  klass1 = G_LOCAL_DIRECTORY_MONITOR_CLASS (g_type_class_ref (*type1));
+  klass2 = G_LOCAL_DIRECTORY_MONITOR_CLASS (g_type_class_ref (*type2));
+
+  ret = klass1->prio - klass2->prio;
+
+  g_type_class_unref (klass1);
+  g_type_class_unref (klass2);
+
+  return ret;
+}
+
+extern GType g_inotify_directory_monitor_get_type (void);
+
+static gpointer
+get_default_local_directory_monitor (gpointer data)
+{
+  GType *monitor_impls, chosen_type;
+  guint n_monitor_impls;
+  GType *ret = (GType *) data;
+  gint i;
+
+#if defined(HAVE_SYS_INOTIFY_H) || defined(HAVE_LINUX_INOTIFY_H)
+  /* Register Inotify monitor */
+  g_inotify_directory_monitor_get_type ();
+#endif
+
+  g_io_modules_ensure_loaded (GIO_MODULE_DIR);
+  
+  monitor_impls = g_type_children (G_TYPE_LOCAL_DIRECTORY_MONITOR,
+                                   &n_monitor_impls);
+
+  chosen_type = G_TYPE_INVALID;
+
+  g_qsort_with_data (monitor_impls,
+                     n_monitor_impls,
+                     sizeof (GType),
+                     _compare_monitor_class_by_prio,
+                     NULL);
+
+  for (i = n_monitor_impls - 1; i >= 0 && chosen_type == G_TYPE_INVALID; i--)
+    {    
+      GLocalDirectoryMonitorClass *klass;
+
+      klass = G_LOCAL_DIRECTORY_MONITOR_CLASS (g_type_class_ref (monitor_impls[i]));
+
+      if (klass->is_supported())
+        chosen_type = monitor_impls[i];
+
+      g_type_class_unref (klass);
+    }
+
+  g_free (monitor_impls);
+  *ret = chosen_type;
+
+  return NULL;
+}
+
+/**
+ * g_local_directory_monitor_new:
+ * @dirname: filename of the directory to monitor.
+ * @flags: #GFileMonitorFlags.
+ * 
+ * Returns: new #GDirectoryMonitor for the given @dirname.
+ **/
+GDirectoryMonitor*
+g_local_directory_monitor_new (const char* dirname,
+                               GFileMonitorFlags flags)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  static GType monitor_type = G_TYPE_INVALID;
+
+  g_once (&once_init, get_default_local_directory_monitor, &monitor_type);
+
+  if (monitor_type != G_TYPE_INVALID)
+    return G_DIRECTORY_MONITOR (g_object_new (monitor_type, "dirname", dirname, NULL));
+
+  return NULL;
+}
+
+static gboolean
+g_local_directory_monitor_cancel (GDirectoryMonitor* monitor)
+{
+  GLocalDirectoryMonitor *local_monitor = G_LOCAL_DIRECTORY_MONITOR (monitor);
+
+  if (local_monitor->mount_monitor)
+    {
+      g_signal_handlers_disconnect_by_func (local_monitor->mount_monitor, mounts_changed, local_monitor);
+      g_object_unref (local_monitor->mount_monitor);
+      local_monitor->mount_monitor = NULL;
+    }
+
+  return TRUE;
+}
+
diff --git a/gio/glocaldirectorymonitor.h b/gio/glocaldirectorymonitor.h
new file mode 100644 (file)
index 0000000..9b8f9ed
--- /dev/null
@@ -0,0 +1,65 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_DIRECTORY_MONITOR_H__
+#define __G_LOCAL_DIRECTORY_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gdirectorymonitor.h>
+
+#include "gunixmounts.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_DIRECTORY_MONITOR         (g_local_directory_monitor_get_type ())
+#define G_LOCAL_DIRECTORY_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_DIRECTORY_MONITOR, GLocalDirectoryMonitor))
+#define G_LOCAL_DIRECTORY_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_LOCAL_DIRECTORY_MONITOR, GLocalDirectoryMonitorClass))
+#define G_IS_LOCAL_DIRECTORY_MONITOR(o)                (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_DIRECTORY_MONITOR))
+#define G_IS_LOCAL_DIRECTORY_MONITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_DIRECTORY_MONITOR))
+
+typedef struct _GLocalDirectoryMonitor      GLocalDirectoryMonitor;
+typedef struct _GLocalDirectoryMonitorClass GLocalDirectoryMonitorClass;
+
+struct _GLocalDirectoryMonitor
+{
+  GDirectoryMonitor parent_instance;
+  gchar *dirname;
+  /* For mount emulation */
+  GUnixMountMonitor *mount_monitor;
+  gboolean was_mounted;
+};
+
+struct _GLocalDirectoryMonitorClass {
+  GDirectoryMonitorClass parent_class;
+  gint prio;
+  gboolean mount_notify;
+  gboolean (*is_supported) (void);
+};
+
+GType g_local_directory_monitor_get_type (void) G_GNUC_CONST;
+
+GDirectoryMonitor* g_local_directory_monitor_new (const char* dirname,
+                                                 GFileMonitorFlags flags);
+
+G_END_DECLS
+
+#endif /* __G_LOCAL_DIRECTORY_MONITOR_H__ */
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
new file mode 100644 (file)
index 0000000..9d5b926
--- /dev/null
@@ -0,0 +1,1826 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#if HAVE_SYS_STATFS_H
+#include <sys/statfs.h>
+#endif
+#if HAVE_SYS_STATVFS_H
+#include <sys/statvfs.h>
+#endif
+#if HAVE_SYS_VFS_H
+#include <sys/vfs.h>
+#elif HAVE_SYS_MOUNT_H
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <sys/mount.h>
+#endif
+
+#if defined(HAVE_STATFS) && defined(HAVE_STATVFS)
+/* Some systems have both statfs and statvfs, pick the
+   most "native" for these */
+# if defined(sun) && defined(__SVR4)
+   /* on solaris, statfs doesn't even have the
+      f_bavail field */
+#  define USE_STATVFS
+# else
+  /* at least on linux, statfs is the actual syscall */
+#  define USE_STATFS
+# endif
+
+#elif defined(HAVE_STATFS)
+
+# define USE_STATFS
+
+#elif defined(HAVE_STATVFS)
+
+# define USE_STATVFS
+
+#endif
+
+#include "glocalfile.h"
+#include "glocalfileinfo.h"
+#include "glocalfileenumerator.h"
+#include "glocalfileinputstream.h"
+#include "glocalfileoutputstream.h"
+#include "glocaldirectorymonitor.h"
+#include "glocalfilemonitor.h"
+#include "gvolumeprivate.h"
+#include <glib/gstdio.h>
+#include "glibintl.h"
+
+static void g_local_file_file_iface_init (GFileIface       *iface);
+
+static GFileAttributeInfoList *local_writable_attributes = NULL;
+static GFileAttributeInfoList *local_writable_namespaces = NULL;
+
+struct _GLocalFile
+{
+  GObject parent_instance;
+
+  char *filename;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GLocalFile, g_local_file, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_FILE,
+                                               g_local_file_file_iface_init))
+
+static char * find_mountpoint_for (const char *file, dev_t dev);
+
+static void
+g_local_file_finalize (GObject *object)
+{
+  GLocalFile *local;
+
+  local = G_LOCAL_FILE (object);
+
+  g_free (local->filename);
+  
+  if (G_OBJECT_CLASS (g_local_file_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_local_file_parent_class)->finalize) (object);
+}
+
+static void
+g_local_file_class_init (GLocalFileClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GFileAttributeInfoList *list;
+
+  gobject_class->finalize = g_local_file_finalize;
+
+  /* Set up attribute lists */
+
+  /* Writable attributes: */
+
+  list = g_file_attribute_info_list_new ();
+
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_UNIX_MODE,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+  
+#ifdef HAVE_CHOWN
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_UNIX_UID,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_UNIX_GID,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+#endif
+  
+#ifdef HAVE_SYMLINK
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET,
+                                 G_FILE_ATTRIBUTE_TYPE_BYTE_STRING,
+                                 0);
+#endif
+  
+#ifdef HAVE_UTIMES
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT64,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_TIME_ACCESS,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT64,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+  g_file_attribute_info_list_add (list,
+                                 G_FILE_ATTRIBUTE_TIME_ACCESS_USEC,
+                                 G_FILE_ATTRIBUTE_TYPE_UINT32,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+#endif
+
+  local_writable_attributes = list;
+
+  /* Writable namespaces: */
+  
+  list = g_file_attribute_info_list_new ();
+
+#ifdef HAVE_XATTR
+  g_file_attribute_info_list_add (list,
+                                 "xattr",
+                                 G_FILE_ATTRIBUTE_TYPE_STRING,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WITH_FILE |
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+  g_file_attribute_info_list_add (list,
+                                 "xattr_sys",
+                                 G_FILE_ATTRIBUTE_TYPE_STRING,
+                                 G_FILE_ATTRIBUTE_FLAGS_COPY_WHEN_MOVED);
+#endif
+
+  local_writable_namespaces = list;
+}
+
+static void
+g_local_file_init (GLocalFile *local)
+{
+}
+
+
+static char *
+canonicalize_filename (const char *filename)
+{
+  char *canon, *start, *p, *q;
+  char *cwd;
+  
+  if (!g_path_is_absolute (filename))
+    {
+      cwd = g_get_current_dir ();
+      canon = g_build_filename (cwd, filename, NULL);
+      g_free (cwd);
+    }
+  else
+    canon = g_strdup (filename);
+
+  start = (char *)g_path_skip_root (canon);
+
+  p = start;
+  while (*p != 0)
+    {
+      if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR (p[1])))
+       {
+         memmove (p, p+1, strlen (p+1)+1);
+       }
+      else if (p[0] == '.' && p[1] == '.' && (p[2] == 0 || G_IS_DIR_SEPARATOR (p[2])))
+       {
+         q = p + 2;
+         /* Skip previous separator */
+         p = p - 2;
+         if (p < start)
+           p = start;
+         while (p > start && !G_IS_DIR_SEPARATOR (*p))
+           p--;
+         if (G_IS_DIR_SEPARATOR (*p))
+           *p++ = G_DIR_SEPARATOR;
+         memmove (p, q, strlen (q)+1);
+       }
+      else
+       {
+         /* Skip until next separator */
+         while (*p != 0 && !G_IS_DIR_SEPARATOR (*p))
+           p++;
+         
+         if (*p != 0)
+           {
+             /* Canonicalize one separator */
+             *p++ = G_DIR_SEPARATOR;
+           }
+       }
+
+      /* Remove additional separators */
+      q = p;
+      while (*q && G_IS_DIR_SEPARATOR (*q))
+       q++;
+
+      if (p != q)
+       memmove (p, q, strlen (q)+1);
+    }
+
+  /* Remove trailing slashes */
+  if (p > start && G_IS_DIR_SEPARATOR (*(p-1)))
+    *(p-1) = 0;
+  
+  return canon;
+}
+
+/**
+ * g_local_file_new:
+ * @filename: filename of the file to create.
+ * 
+ * Returns: new local #GFile.
+ **/
+GFile *
+g_local_file_new (const char *filename)
+{
+  GLocalFile *local;
+
+  local = g_object_new (G_TYPE_LOCAL_FILE, NULL);
+  local->filename = canonicalize_filename (filename);
+  
+  return G_FILE (local);
+}
+
+static gboolean
+g_local_file_is_native (GFile *file)
+{
+  return TRUE;
+}
+
+static gboolean
+g_local_file_has_uri_scheme (GFile *file,
+                            const char *uri_scheme)
+{
+  return g_ascii_strcasecmp (uri_scheme, "file") == 0;
+}
+
+static char *
+g_local_file_get_uri_scheme (GFile *file)
+{
+  return g_strdup ("file");
+}
+
+static char *
+g_local_file_get_basename (GFile *file)
+{
+  return g_path_get_basename (G_LOCAL_FILE (file)->filename);
+}
+
+static char *
+g_local_file_get_path (GFile *file)
+{
+  return g_strdup (G_LOCAL_FILE (file)->filename);
+}
+
+static char *
+g_local_file_get_uri (GFile *file)
+{
+  return g_filename_to_uri (G_LOCAL_FILE (file)->filename, NULL, NULL);
+}
+
+static gboolean
+get_filename_charset (const gchar **filename_charset)
+{
+  const gchar **charsets;
+  gboolean is_utf8;
+  
+  is_utf8 = g_get_filename_charsets (&charsets);
+
+  if (filename_charset)
+    *filename_charset = charsets[0];
+  
+  return is_utf8;
+}
+
+static gboolean
+name_is_valid_for_display (const char *string,
+                          gboolean is_valid_utf8)
+{
+  char c;
+  
+  if (!is_valid_utf8 &&
+      !g_utf8_validate (string, -1, NULL))
+    return FALSE;
+
+  while ((c = *string++) != 0)
+    {
+      if (g_ascii_iscntrl(c))
+       return FALSE;
+    }
+
+  return TRUE;
+}
+
+static char *
+g_local_file_get_parse_name (GFile *file)
+{
+  const char *filename;
+  char *parse_name;
+  const gchar *charset;
+  char *utf8_filename;
+  char *roundtripped_filename;
+  gboolean free_utf8_filename;
+  gboolean is_valid_utf8;
+
+  filename = G_LOCAL_FILE (file)->filename;
+  if (get_filename_charset (&charset))
+    {
+      utf8_filename = (char *)filename;
+      free_utf8_filename = FALSE;
+      is_valid_utf8 = FALSE; /* Can't guarantee this */
+    }
+  else
+    {
+      utf8_filename = g_convert (filename, -1, 
+                                "UTF-8", charset, NULL, NULL, NULL);
+      free_utf8_filename = TRUE;
+      is_valid_utf8 = TRUE;
+
+      if (utf8_filename != NULL)
+       {
+         /* Make sure we can roundtrip: */
+         roundtripped_filename = g_convert (utf8_filename, -1,
+                                            charset, "UTF-8", NULL, NULL, NULL);
+         
+         if (roundtripped_filename == NULL ||
+             strcmp (utf8_filename, roundtripped_filename) != 0)
+           {
+             g_free (utf8_filename);
+             utf8_filename = NULL;
+           }
+       }
+    }
+
+
+  if (utf8_filename != NULL &&
+      name_is_valid_for_display (utf8_filename, is_valid_utf8))
+    {
+      if (free_utf8_filename)
+       parse_name = utf8_filename;
+      else
+       parse_name = g_strdup (utf8_filename);
+    }
+  else
+    {
+      parse_name = g_filename_to_uri (filename, NULL, NULL);
+      if (free_utf8_filename)
+       g_free (utf8_filename);
+    }
+  
+  return parse_name;
+}
+
+static GFile *
+g_local_file_get_parent (GFile *file)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  const char *non_root;
+  char *dirname;
+  GFile *parent;
+
+  /* Check for root */
+  non_root = g_path_skip_root (local->filename);
+  if (*non_root == 0)
+    return NULL;
+
+  dirname = g_path_get_dirname (local->filename);
+  parent = g_local_file_new (dirname);
+  g_free (dirname);
+  return parent;
+}
+
+static GFile *
+g_local_file_dup (GFile *file)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+
+  return g_local_file_new (local->filename);
+}
+
+static guint
+g_local_file_hash (GFile *file)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  
+  return g_str_hash (local->filename);
+}
+
+static gboolean
+g_local_file_equal (GFile *file1,
+                   GFile *file2)
+{
+  GLocalFile *local1 = G_LOCAL_FILE (file1);
+  GLocalFile *local2 = G_LOCAL_FILE (file2);
+
+  return g_str_equal (local1->filename, local2->filename);
+}
+
+static const char *
+match_prefix (const char *path, const char *prefix)
+{
+  int prefix_len;
+
+  prefix_len = strlen (prefix);
+  if (strncmp (path, prefix, prefix_len) != 0)
+    return NULL;
+  return path + prefix_len;
+}
+
+static gboolean
+g_local_file_contains_file (GFile *parent,
+                           GFile *descendant)
+{
+  GLocalFile *parent_local = G_LOCAL_FILE (parent);
+  GLocalFile *descendant_local = G_LOCAL_FILE (descendant);
+  const char *remainder;
+
+  remainder = match_prefix (descendant_local->filename, parent_local->filename);
+  if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+    return TRUE;
+  return FALSE;
+}
+
+static char *
+g_local_file_get_relative_path (GFile *parent,
+                               GFile *descendant)
+{
+  GLocalFile *parent_local = G_LOCAL_FILE (parent);
+  GLocalFile *descendant_local = G_LOCAL_FILE (descendant);
+  const char *remainder;
+
+  remainder = match_prefix (descendant_local->filename, parent_local->filename);
+  
+  if (remainder != NULL && G_IS_DIR_SEPARATOR (*remainder))
+    return g_strdup (remainder + 1);
+  return NULL;
+}
+
+static GFile *
+g_local_file_resolve_relative_path (GFile *file,
+                                   const char *relative_path)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  char *filename;
+  GFile *child;
+
+  if (g_path_is_absolute (relative_path))
+    return g_local_file_new (relative_path);
+  
+  filename = g_build_filename (local->filename, relative_path, NULL);
+  child = g_local_file_new (filename);
+  g_free (filename);
+  
+  return child;
+}
+
+static GFileEnumerator *
+g_local_file_enumerate_children (GFile *file,
+                                const char *attributes,
+                                GFileQueryInfoFlags flags,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  return g_local_file_enumerator_new (local->filename,
+                                     attributes, flags,
+                                     cancellable, error);
+}
+
+static GFile *
+g_local_file_get_child_for_display_name (GFile        *file,
+                                        const char   *display_name,
+                                        GError      **error)
+{
+  GFile *parent, *new_file;
+  char *basename;
+
+  basename = g_filename_from_utf8 (display_name, -1, NULL, NULL, NULL);
+  if (basename == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_INVALID_FILENAME,
+                  _("Invalid filename %s"), display_name);
+      return NULL;
+    }
+
+  parent = g_file_get_parent (file);
+  new_file = g_file_get_child (file, basename);
+  g_object_unref (parent);
+  g_free (basename);
+  
+  return new_file;
+}
+
+#ifdef USE_STATFS
+static const char *
+get_fs_type (long f_type)
+{
+
+  /* filesystem ids taken from linux manpage */
+  switch (f_type) {
+  case 0xadf5:
+    return "adfs";
+  case 0xADFF:
+    return "affs";
+  case 0x42465331:
+    return "befs";
+  case 0x1BADFACE:
+    return "bfs";
+  case 0xFF534D42:
+    return "cifs";
+  case 0x73757245:
+    return "coda";
+  case 0x012FF7B7:
+    return "coh";
+  case 0x28cd3d45:
+    return "cramfs";
+  case 0x1373:
+    return "devfs";
+  case 0x00414A53:
+    return "efs";
+  case 0x137D:
+    return "ext";
+  case 0xEF51:
+    return "ext2";
+  case 0xEF53:
+    return "ext3";
+  case 0x4244:
+    return "hfs";
+  case 0xF995E849:
+    return "hpfs";
+  case 0x958458f6:
+    return "hugetlbfs";
+  case 0x9660:
+    return "isofs";
+  case 0x72b6:
+    return "jffs2";
+  case 0x3153464a:
+    return "jfs";
+  case 0x137F:
+    return "minix";
+  case 0x138F:
+    return "minix2";
+  case 0x2468:
+    return "minix2";
+  case 0x2478:
+    return "minix22";
+  case 0x4d44:
+    return "msdos";
+  case 0x564c:
+    return "ncp";
+  case 0x6969:
+    return "nfs";
+  case 0x5346544e:
+    return "ntfs";
+  case 0x9fa1:
+    return "openprom";
+  case 0x9fa0:
+    return "proc";
+  case 0x002f:
+    return "qnx4";
+  case 0x52654973:
+    return "reiserfs";
+  case 0x7275:
+    return "romfs";
+  case 0x517B:
+    return "smb";
+  case 0x012FF7B6:
+    return "sysv2";
+  case 0x012FF7B5:
+    return "sysv4";
+  case 0x01021994:
+    return "tmpfs";
+  case 0x15013346:
+    return "udf";
+  case 0x00011954:
+    return "ufs";
+  case 0x9fa2:
+    return "usbdevice";
+  case 0xa501FCF5:
+    return "vxfs";
+  case 0x012FF7B4:
+    return "xenix";
+  case 0x58465342:
+    return "xfs";
+  case 0x012FD16D:
+    return "xiafs";
+  default:
+    return NULL;
+  }
+}
+#endif
+
+G_LOCK_DEFINE_STATIC(mount_info_hash);
+static GHashTable *mount_info_hash = NULL;
+guint64 mount_info_hash_cache_time = 0;
+
+typedef enum {
+  MOUNT_INFO_READONLY = 1<<0
+} MountInfo;
+
+static gboolean
+device_equal (gconstpointer v1,
+          gconstpointer v2)
+{
+  return *(dev_t *)v1 == * (dev_t *)v2;
+}
+
+static guint
+device_hash (gconstpointer  v)
+{
+  return (guint) *(dev_t *)v;
+}
+
+static void
+get_mount_info (GFileInfo *fs_info,
+               const char *path,
+               GFileAttributeMatcher *matcher)
+{
+  struct stat buf;
+  gboolean got_info;
+  gpointer info_as_ptr;
+  guint mount_info;
+  char *mountpoint;
+  dev_t *dev;
+  GUnixMount *mount;
+  guint64 cache_time;
+
+  if (lstat (path, &buf) != 0)
+    return;
+
+  G_LOCK (mount_info_hash);
+
+  if (mount_info_hash == NULL)
+    mount_info_hash = g_hash_table_new_full (device_hash, device_equal,
+                                            g_free, NULL);
+
+
+  if (g_unix_mounts_changed_since (mount_info_hash_cache_time))
+    g_hash_table_remove_all (mount_info_hash);
+  
+  got_info = g_hash_table_lookup_extended (mount_info_hash,
+                                          &buf.st_dev,
+                                          NULL,
+                                          &info_as_ptr);
+  
+  G_UNLOCK (mount_info_hash);
+  
+  mount_info = GPOINTER_TO_UINT (info_as_ptr);
+  
+  if (!got_info)
+    {
+      mount_info = 0;
+
+      mountpoint = find_mountpoint_for (path, buf.st_dev);
+      if (mountpoint == NULL)
+       mountpoint = "/";
+
+      mount = g_get_unix_mount_at (mountpoint, &cache_time);
+      if (mount)
+       {
+         if (g_unix_mount_is_readonly (mount))
+           mount_info |= MOUNT_INFO_READONLY;
+         
+         g_unix_mount_free (mount);
+       }
+
+      dev = g_new0 (dev_t, 1);
+      *dev = buf.st_dev;
+      
+      G_LOCK (mount_info_hash);
+      mount_info_hash_cache_time = cache_time;
+      g_hash_table_insert (mount_info_hash, dev, GUINT_TO_POINTER (mount_info));
+      G_UNLOCK (mount_info_hash);
+    }
+
+  if (mount_info & MOUNT_INFO_READONLY)
+    g_file_info_set_attribute_boolean (fs_info, G_FILE_ATTRIBUTE_FS_READONLY, TRUE);
+}
+
+static GFileInfo *
+g_local_file_query_filesystem_info (GFile                *file,
+                                   const char           *attributes,
+                                   GCancellable         *cancellable,
+                                   GError              **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  GFileInfo *info;
+  int statfs_result;
+  gboolean no_size;
+  guint64 block_size;
+#ifdef USE_STATFS
+  struct statfs statfs_buffer;
+  const char *fstype;
+#elif defined(USE_STATVFS)
+  struct statvfs statfs_buffer;
+#endif
+  GFileAttributeMatcher *attribute_matcher;
+       
+  no_size = FALSE;
+  
+#ifdef USE_STATFS
+  
+#if STATFS_ARGS == 2
+  statfs_result = statfs (local->filename, &statfs_buffer);
+#elif STATFS_ARGS == 4
+  statfs_result = statfs (local->filename, &statfs_buffer,
+                         sizeof (statfs_buffer), 0);
+#endif
+  block_size = statfs_buffer.f_bsize;
+  
+#if defined(__linux__)
+  /* ncpfs does not know the amount of available and free space *
+   * assuming ncpfs is linux specific, if you are on a non-linux platform
+   * where ncpfs is available, please file a bug about it on bugzilla.gnome.org
+   */
+  if (statfs_buffer.f_bavail == 0 && statfs_buffer.f_bfree == 0 &&
+      /* linux/ncp_fs.h: NCP_SUPER_MAGIC == 0x564c */
+      statfs_buffer.f_type == 0x564c)
+    no_size = TRUE;
+#endif
+  
+#elif defined(USE_STATVFS)
+  statfs_result = statvfs (local->filename, &statfs_buffer);
+  block_size = statfs_buffer.f_frsize; 
+#endif
+
+  if (statfs_result == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error getting filesystem info: %s"),
+                  g_strerror (errno));
+      return NULL;
+    }
+
+  info = g_file_info_new ();
+
+  attribute_matcher = g_file_attribute_matcher_new (attributes);
+  
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_FS_FREE))
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FS_FREE, block_size * statfs_buffer.f_bavail);
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_FS_SIZE))
+    g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_FS_SIZE, block_size * statfs_buffer.f_blocks);
+
+#ifdef USE_STATFS
+  fstype = get_fs_type (statfs_buffer.f_type);
+  if (fstype &&
+      g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_FS_TYPE))
+    g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_FS_TYPE, fstype);
+#endif  
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_FS_READONLY))
+    {
+      get_mount_info (info, local->filename, attribute_matcher);
+    }
+  
+  g_file_attribute_matcher_unref (attribute_matcher);
+  
+  return info;
+}
+
+static GVolume *
+g_local_file_find_enclosing_volume (GFile *file,
+                                   GCancellable *cancellable,
+                                   GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  struct stat buf;
+  char *mountpoint;
+  GVolume *volume;
+
+  if (lstat (local->filename, &buf) != 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_FOUND,
+                  _("Containing volume does not exist"));
+      return NULL;
+    }
+
+  mountpoint = find_mountpoint_for (local->filename, buf.st_dev);
+  if (mountpoint == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_FOUND,
+                  _("Containing volume does not exist"));
+      return NULL;
+    }
+
+  volume = g_volume_get_for_mount_path (mountpoint);
+  g_free (mountpoint);
+  if (volume)
+    return volume;
+
+  g_set_error (error, G_IO_ERROR,
+              G_IO_ERROR_NOT_FOUND,
+              _("Containing volume does not exist"));
+  return NULL;
+}
+
+static GFile *
+g_local_file_set_display_name (GFile *file,
+                              const char *display_name,
+                              GCancellable *cancellable,
+                              GError **error)
+{
+  GLocalFile *local, *new_local;
+  GFile *new_file, *parent;
+  struct stat statbuf;
+  int errsv;
+
+  parent = g_file_get_parent (file);
+  if (parent == NULL)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_FAILED,
+                  _("Can't rename root directory"));
+      return NULL;
+    }
+  
+  new_file = g_file_get_child_for_display_name  (parent, display_name, error);
+  g_object_unref (parent);
+  
+  if (new_file == NULL)
+    return NULL;
+  
+  local = G_LOCAL_FILE (file);
+  new_local = G_LOCAL_FILE (new_file);
+
+  if (!(g_lstat (new_local->filename, &statbuf) == -1 &&
+       errno == ENOENT))
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_EXISTS,
+                  _("Can't rename file, filename already exist"));
+      return NULL;
+    }
+
+  if (rename (local->filename, new_local->filename) == -1)
+    {
+      errsv = errno;
+
+      if (errsv == EINVAL)
+       /* We can't get a rename file into itself error herer,
+          so this must be an invalid filename, on e.g. FAT */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error renaming file: %s"),
+                    g_strerror (errsv));
+      g_object_unref (new_file);
+      return NULL;
+    }
+  
+  return new_file;
+}
+
+static GFileInfo *
+g_local_file_query_info (GFile                *file,
+                        const char           *attributes,
+                        GFileQueryInfoFlags   flags,
+                        GCancellable         *cancellable,
+                        GError              **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  GFileInfo *info;
+  GFileAttributeMatcher *matcher;
+  char *basename, *dirname;
+  GLocalParentFileInfo parent_info;
+
+  matcher = g_file_attribute_matcher_new (attributes);
+  
+  basename = g_path_get_basename (local->filename);
+  
+  dirname = g_path_get_dirname (local->filename);
+  _g_local_file_info_get_parent_info (dirname, matcher, &parent_info);
+  g_free (dirname);
+  
+  info = _g_local_file_info_get (basename, local->filename,
+                                matcher, flags, &parent_info,
+                                error);
+  
+  g_free (basename);
+
+  g_file_attribute_matcher_unref (matcher);
+
+  return info;
+}
+
+static GFileAttributeInfoList *
+g_local_file_query_settable_attributes (GFile                      *file,
+                                       GCancellable               *cancellable,
+                                       GError                    **error)
+{
+  return g_file_attribute_info_list_ref (local_writable_attributes);
+}
+
+static GFileAttributeInfoList *
+g_local_file_query_writable_namespaces (GFile *file,
+                                       GCancellable *cancellable,
+                                       GError **error)
+{
+  return g_file_attribute_info_list_ref (local_writable_namespaces);
+}
+
+static gboolean
+g_local_file_set_attribute (GFile *file,
+                           const char *attribute,
+                           const GFileAttributeValue *value,
+                           GFileQueryInfoFlags flags,
+                           GCancellable *cancellable,
+                           GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+
+  return _g_local_file_info_set_attribute (local->filename,
+                                          attribute,
+                                          value,
+                                          flags,
+                                          cancellable,
+                                          error);
+}
+
+static gboolean
+g_local_file_set_attributes_from_info (GFile *file,
+                                      GFileInfo *info,
+                                      GFileQueryInfoFlags flags,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  int res, chained_res;
+  GFileIface* default_iface;
+
+  res = _g_local_file_info_set_attributes (local->filename,
+                                          info, flags, 
+                                          cancellable,
+                                          error);
+
+  if (!res)
+    error = NULL; /* Don't write over error if further errors */
+
+  default_iface = g_type_default_interface_peek (G_TYPE_FILE);
+
+  chained_res = (default_iface->set_attributes_from_info) (file, info, flags, cancellable, error);
+  
+  return res && chained_res;
+}
+
+static GFileInputStream *
+g_local_file_read (GFile *file,
+                  GCancellable *cancellable,
+                  GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  int fd;
+  struct stat buf;
+  
+  fd = g_open (local->filename, O_RDONLY, 0);
+  if (fd == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error opening file: %s"),
+                  g_strerror (errno));
+      return NULL;
+    }
+
+  if (fstat(fd, &buf) == 0 && S_ISDIR (buf.st_mode))
+    {
+      close (fd);
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_IS_DIRECTORY,
+                  _("Can't open directory"));
+      return NULL;
+    }
+  
+  return g_local_file_input_stream_new (fd);
+}
+
+static GFileOutputStream *
+g_local_file_append_to (GFile *file,
+                       GFileCreateFlags flags,
+                       GCancellable *cancellable,
+                       GError **error)
+{
+  return g_local_file_output_stream_append (G_LOCAL_FILE (file)->filename,
+                                           flags, cancellable, error);
+}
+
+static GFileOutputStream *
+g_local_file_create (GFile *file,
+                    GFileCreateFlags flags,
+                    GCancellable *cancellable,
+                    GError **error)
+{
+  return g_local_file_output_stream_create (G_LOCAL_FILE (file)->filename,
+                                           flags, cancellable, error);
+}
+
+static GFileOutputStream *
+g_local_file_replace (GFile *file,
+                     const char *etag,
+                     gboolean make_backup,
+                     GFileCreateFlags flags,
+                     GCancellable *cancellable,
+                     GError **error)
+{
+  return g_local_file_output_stream_replace (G_LOCAL_FILE (file)->filename,
+                                            etag, make_backup, flags,
+                                            cancellable, error);
+}
+
+
+static gboolean
+g_local_file_delete (GFile *file,
+                    GCancellable *cancellable,
+                    GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  
+  if (g_remove (local->filename) == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error removing file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static char *
+strip_trailing_slashes (const char *path)
+{
+  char *path_copy;
+  int len;
+
+  path_copy = g_strdup (path);
+  len = strlen (path_copy);
+  while (len > 1 && path_copy[len-1] == '/')
+    path_copy[--len] = 0;
+
+  return path_copy;
+ }
+
+static char *
+expand_symlink (const char *link)
+{
+  char *resolved, *canonical, *parent, *link2;
+  char symlink_value[4096];
+  ssize_t res;
+  
+  res = readlink (link, symlink_value, sizeof (symlink_value) - 1);
+  if (res == -1)
+    return g_strdup (link);
+  symlink_value[res] = 0;
+  
+  if (g_path_is_absolute (symlink_value))
+    return canonicalize_filename (symlink_value);
+  else
+    {
+      link2 = strip_trailing_slashes (link);
+      parent = g_path_get_dirname (link2);
+      g_free (link2);
+      
+      resolved = g_build_filename (parent, symlink_value, NULL);
+      g_free (parent);
+      
+      canonical = canonicalize_filename (resolved);
+      
+      g_free (resolved);
+
+      return canonical;
+    }
+}
+
+static char *
+get_parent (const char *path, dev_t *parent_dev)
+{
+  char *parent, *tmp;
+  struct stat parent_stat;
+  int num_recursions;
+  char *path_copy;
+
+  path_copy = strip_trailing_slashes (path);
+  
+  parent = g_path_get_dirname (path_copy);
+  if (strcmp (parent, ".") == 0 ||
+      strcmp (parent, path_copy) == 0)
+    {
+      g_free (path_copy);
+      return NULL;
+    }
+  g_free (path_copy);
+
+  num_recursions = 0;
+  do {
+    if (g_lstat (parent, &parent_stat) != 0)
+      {
+       g_free (parent);
+       return NULL;
+      }
+    
+    if (S_ISLNK (parent_stat.st_mode))
+      {
+       tmp = parent;
+       parent = expand_symlink (parent);
+       g_free (tmp);
+      }
+    
+    num_recursions++;
+    if (num_recursions > 12)
+      {
+       g_free (parent);
+       return NULL;
+      }
+  } while (S_ISLNK (parent_stat.st_mode));
+
+  *parent_dev = parent_stat.st_dev;
+  
+  return parent;
+}
+
+static char *
+expand_all_symlinks (const char *path)
+{
+  char *parent, *parent_expanded;
+  char *basename, *res;
+  dev_t parent_dev;
+
+  parent = get_parent (path, &parent_dev);
+  if (parent)
+    {
+      parent_expanded = expand_all_symlinks (parent);
+      g_free (parent);
+      basename = g_path_get_basename (path);
+      res = g_build_filename (parent_expanded, basename, NULL);
+      g_free (basename);
+      g_free (parent_expanded);
+    }
+  else
+    res = g_strdup (path);
+  
+  return res;
+}
+
+static char *
+find_mountpoint_for (const char *file, dev_t dev)
+{
+  char *dir, *parent;
+  dev_t dir_dev, parent_dev;
+
+  dir = g_strdup (file);
+  dir_dev = dev;
+
+  while (1) {
+    parent = get_parent (dir, &parent_dev);
+    if (parent == NULL)
+      return dir;
+    
+    if (parent_dev != dir_dev)
+      {
+       g_free (parent);
+       return dir;
+      }
+    
+    g_free (dir);
+    dir = parent;
+  }
+}
+
+static char *
+find_topdir_for (const char *file)
+{
+  char *dir;
+  dev_t dir_dev;
+
+  dir = get_parent (file, &dir_dev);
+  if (dir == NULL)
+    return NULL;
+
+  return find_mountpoint_for (dir, dir_dev);
+}
+
+static char *
+get_unique_filename (const char *basename, int id)
+{
+  const char *dot;
+      
+  if (id == 1)
+    return g_strdup (basename);
+
+  dot = strchr (basename, '.');
+  if (dot)
+    return g_strdup_printf ("%.*s.%d%s", dot - basename, basename, id, dot);
+  else
+    return g_strdup_printf ("%s.%d", basename, id);
+}
+
+static gboolean
+path_has_prefix (const char *path, const char *prefix)
+{
+  int prefix_len;
+
+  if (prefix == NULL)
+    return TRUE;
+
+  prefix_len = strlen (prefix);
+  
+  if (strncmp (path, prefix, prefix_len) == 0 &&
+      (prefix_len == 0 || /* empty prefix always matches */
+       prefix[prefix_len - 1] == '/' || /* last char in prefix was a /, so it must be in path too */
+       path[prefix_len] == 0 ||
+       path[prefix_len] == '/'))
+    return TRUE;
+  
+  return FALSE;
+}
+
+static char *
+try_make_relative (const char *path, const char *base)
+{
+  char *path2, *base2;
+  char *relative;
+
+  path2 = expand_all_symlinks (path);
+  base2 = expand_all_symlinks (base);
+
+  relative = NULL;
+  if (path_has_prefix (path2, base2))
+    {
+      relative = path2 + strlen (base2);
+      while (*relative == '/')
+       relative ++;
+      relative = g_strdup (relative);
+    }
+  g_free (path2);
+  g_free (base2);
+
+  if (relative)
+    return relative;
+  
+  /* Failed, use abs path */
+  return g_strdup (path);
+}
+
+static char *
+escape_trash_name (char *name)
+{
+  GString *str;
+  const gchar hex[16] = "0123456789ABCDEF";
+  
+  str = g_string_new ("");
+
+  while (*name != 0)
+    {
+      char c;
+
+      c = *name++;
+
+      if (g_ascii_isprint (c))
+       g_string_append_c (str, c);
+      else
+       {
+          g_string_append_c (str, '%');
+          g_string_append_c (str, hex[((guchar)c) >> 4]);
+          g_string_append_c (str, hex[((guchar)c) & 0xf]);
+       }
+    }
+
+  return g_string_free (str, FALSE);
+}
+
+static gboolean
+g_local_file_trash (GFile *file,
+                   GCancellable *cancellable,
+                   GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  struct stat file_stat, home_stat, trash_stat, global_stat;
+  const char *homedir;
+  char *dirname;
+  char *trashdir, *globaldir, *topdir, *infodir, *filesdir;
+  char *basename, *trashname, *trashfile, *infoname, *infofile;
+  char *original_name, *original_name_escaped;
+  uid_t uid;
+  char uid_str[32];
+  int i;
+  char *data;
+  gboolean is_homedir_trash;
+  time_t t;
+  struct tm now;
+  char delete_time[32];
+  int fd;
+  
+  if (g_lstat (local->filename, &file_stat) != 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error trashing file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+    
+  homedir = g_get_home_dir ();
+  g_stat (homedir, &home_stat);
+
+  is_homedir_trash = FALSE;
+  trashdir = NULL;
+  if (file_stat.st_dev == home_stat.st_dev)
+    {
+      is_homedir_trash = TRUE;
+      errno = 0;
+      trashdir = g_build_filename (g_get_user_data_dir (), "Trash", NULL);
+      if (g_mkdir_with_parents (trashdir, 0700) < 0)
+       {
+          char *display_name;
+          int err;
+
+          err = errno;
+          display_name = g_filename_display_name (trashdir);
+          g_set_error (error, G_IO_ERROR,
+                       g_io_error_from_errno (err),
+                       _("Unable to create trash dir %s: %s"),
+                       display_name, g_strerror (err));
+          g_free (display_name);
+          g_free (trashdir);
+          return FALSE;
+       }
+      topdir = g_strdup (g_get_user_data_dir ());
+    }
+  else
+    {
+      uid = geteuid ();
+      g_snprintf (uid_str, sizeof (uid_str), "%lu", (unsigned long)uid);
+      
+      topdir = find_topdir_for (local->filename);
+      if (topdir == NULL)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      G_IO_ERROR_NOT_SUPPORTED,
+                      _("Unable to find toplevel directory for trash"));
+         return FALSE;
+       }
+      
+      /* Try looking for global trash dir $topdir/.Trash/$uid */
+      globaldir = g_build_filename (topdir, ".Trash", NULL);
+      if (g_lstat (globaldir, &global_stat) == 0 &&
+         S_ISDIR (global_stat.st_mode) &&
+         (global_stat.st_mode & S_ISVTX) != 0)
+       {
+         trashdir = g_build_filename (globaldir, uid_str, NULL);
+
+         if (g_lstat (trashdir, &trash_stat) == 0)
+           {
+             if (!S_ISDIR (trash_stat.st_mode) ||
+                 trash_stat.st_uid != uid)
+               {
+                 /* Not a directory or not owned by user, ignore */
+                 g_free (trashdir);
+                 trashdir = NULL;
+               }
+           }
+         else if (g_mkdir (trashdir, 0700) == -1)
+           {
+             g_free (trashdir);
+             trashdir = NULL;
+           }
+       }
+      g_free (globaldir);
+
+      if (trashdir == NULL)
+       {
+         /* No global trash dir, or it failed the tests, fall back to $topdir/.Trash-$uid */
+         dirname = g_strdup_printf (".Trash-%s", uid_str);
+         trashdir = g_build_filename (topdir, dirname, NULL);
+         g_free (dirname);
+         
+         if (g_lstat (trashdir, &trash_stat) == 0)
+           {
+             if (!S_ISDIR (trash_stat.st_mode) ||
+                 trash_stat.st_uid != uid)
+               {
+                 /* Not a directory or not owned by user, ignore */
+                 g_free (trashdir);
+                 trashdir = NULL;
+               }
+           }
+         else if (g_mkdir (trashdir, 0700) == -1)
+           {
+             g_free (trashdir);
+             trashdir = NULL;
+           }
+       }
+
+      if (trashdir == NULL)
+       {
+         g_free (topdir);
+         g_set_error (error, G_IO_ERROR,
+                      G_IO_ERROR_NOT_SUPPORTED,
+                      _("Unable to find or create trash directory"));
+         return FALSE;
+       }
+    }
+
+  /* Trashdir points to the trash dir with the "info" and "files" subdirectories */
+
+  infodir = g_build_filename (trashdir, "info", NULL);
+  filesdir = g_build_filename (trashdir, "files", NULL);
+  g_free (trashdir);
+
+  /* Make sure we have the subdirectories */
+  if ((g_mkdir (infodir, 0700) == -1 && errno != EEXIST) ||
+      (g_mkdir (filesdir, 0700) == -1 && errno != EEXIST))
+    {
+      g_free (topdir);
+      g_free (infodir);
+      g_free (filesdir);
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SUPPORTED,
+                  _("Unable to find or create trash directory"));
+      return FALSE;
+    }  
+
+  basename = g_path_get_basename (local->filename);
+  i = 1;
+  trashname = NULL;
+  infofile = NULL;
+  do {
+    g_free (trashname);
+    g_free (infofile);
+    
+    trashname = get_unique_filename (basename, i++);
+    infoname = g_strconcat (trashname, ".trashinfo", NULL);
+    infofile = g_build_filename (infodir, infoname, NULL);
+    g_free (infoname);
+
+    fd = open (infofile, O_CREAT | O_EXCL, 0666);
+  } while (fd == -1 && errno == EEXIST);
+
+  g_free (basename);
+  g_free (infodir);
+
+  if (fd == -1)
+    {
+      g_free (filesdir);
+      g_free (topdir);
+      g_free (trashname);
+      g_free (infofile);
+      
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Unable to create trashed file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+
+  close (fd);
+
+  /* TODO: Maybe we should verify that you can delete the file from the trash
+     before moving it? OTOH, that is hard, as it needs a recursive scan */
+
+  trashfile = g_build_filename (filesdir, trashname, NULL);
+
+  g_free (filesdir);
+
+  if (g_rename (local->filename, trashfile) == -1)
+    {
+      g_free (topdir);
+      g_free (trashname);
+      g_free (infofile);
+      g_free (trashfile);
+      
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Unable to trash file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+
+  g_free (trashfile);
+
+  /* TODO: Do we need to update mtime/atime here after the move? */
+
+  /* Use absolute names for homedir */
+  if (is_homedir_trash)
+    original_name = g_strdup (local->filename);
+  else
+    original_name = try_make_relative (local->filename, topdir);
+  original_name_escaped = escape_trash_name (original_name);
+  
+  g_free (original_name);
+  g_free (topdir);
+  
+  t = time (NULL);
+  localtime_r (&t, &now);
+  delete_time[0] = 0;
+  strftime(delete_time, sizeof (delete_time), "%Y-%m-%dT%H:%M:%S", &now);
+
+  data = g_strdup_printf ("[Trash Info]\nPath=%s\nDeletionDate=%s\n",
+                         original_name_escaped, delete_time);
+
+  g_file_set_contents (infofile, data, -1, NULL);
+  g_free (infofile);
+  g_free (data);
+  
+  g_free (original_name_escaped);
+  g_free (trashname);
+  
+  return TRUE;
+}
+
+static gboolean
+g_local_file_make_directory (GFile *file,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+  GLocalFile *local = G_LOCAL_FILE (file);
+  
+  if (g_mkdir (local->filename, 0755) == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINVAL)
+       /* This must be an invalid filename, on e.g. FAT */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error removing file: %s"),
+                    g_strerror (errsv));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static gboolean
+g_local_file_make_symbolic_link (GFile *file,
+                                const char *symlink_value,
+                                GCancellable *cancellable,
+                                GError **error)
+{
+#ifdef HAVE_SYMLINK
+  GLocalFile *local = G_LOCAL_FILE (file);
+  
+  if (symlink (symlink_value, local->filename) == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINVAL)
+       /* This must be an invalid filename, on e.g. FAT */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error making symbolic link: %s"),
+                    g_strerror (errsv));
+      return FALSE;
+    }
+  return TRUE;
+#else
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Symlinks not supported");
+  return FALSE;
+#endif
+}
+
+
+static gboolean
+g_local_file_copy (GFile                *source,
+                  GFile                *destination,
+                  GFileCopyFlags        flags,
+                  GCancellable         *cancellable,
+                  GFileProgressCallback progress_callback,
+                  gpointer              progress_callback_data,
+                  GError              **error)
+{
+  /* Fall back to default copy */
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Copy not supported");
+  return FALSE;
+}
+
+static gboolean
+g_local_file_move (GFile                *source,
+                  GFile                *destination,
+                  GFileCopyFlags        flags,
+                  GCancellable         *cancellable,
+                  GFileProgressCallback progress_callback,
+                  gpointer              progress_callback_data,
+                  GError              **error)
+{
+  GLocalFile *local_source = G_LOCAL_FILE (source);
+  GLocalFile *local_destination = G_LOCAL_FILE (destination);
+  struct stat statbuf;
+  gboolean destination_exist, source_is_dir;
+  char *backup_name;
+  int res;
+
+  res = g_lstat (local_source->filename, &statbuf);
+  if (res == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error moving file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  else
+    source_is_dir = S_ISDIR (statbuf.st_mode);
+  
+  destination_exist = FALSE;
+  res = g_lstat (local_destination->filename, &statbuf);
+  if (res == 0)
+    {
+      destination_exist = TRUE; /* Target file exists */
+
+      if (flags & G_FILE_COPY_OVERWRITE)
+       {
+         /* Always fail on dirs, even with overwrite */
+         if (S_ISDIR (statbuf.st_mode))
+           {
+             g_set_error (error,
+                          G_IO_ERROR,
+                          G_IO_ERROR_WOULD_MERGE,
+                          _("Can't move directory over directory"));
+             return FALSE;
+           }
+       }
+      else
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_EXISTS,
+                      _("Target file already exists"));
+         return FALSE;
+       }
+    }
+  
+  if (flags & G_FILE_COPY_BACKUP && destination_exist)
+    {
+      backup_name = g_strconcat (local_destination->filename, "~", NULL);
+      if (rename (local_destination->filename, backup_name) == -1)
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_CANT_CREATE_BACKUP,
+                      _("Backup file creation failed"));
+         g_free (backup_name);
+         return FALSE;
+       }
+      g_free (backup_name);
+      destination_exist = FALSE; /* It did, but no more */
+    }
+
+  if (source_is_dir && destination_exist && (flags & G_FILE_COPY_OVERWRITE))
+    {
+      /* Source is a dir, destination exists (and is not a dir, because that would have failed
+        earlier), and we're overwriting. Manually remove the target so we can do the rename. */
+      res = unlink (local_destination->filename);
+      if (res == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error removing target file: %s"),
+                      g_strerror (errno));
+         return FALSE;
+       }
+    }
+  
+  if (rename (local_source->filename, local_destination->filename) == -1)
+    {
+      int errsv = errno;
+      if (errsv == EXDEV)
+       goto fallback;
+
+      if (errsv == EINVAL)
+       /* This must be an invalid filename, on e.g. FAT, or
+          we're trying to move the file into itself...
+          We return invalid filename for both... */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error moving file: %s"),
+                    g_strerror (errsv));
+      return FALSE;
+
+    }
+  return TRUE;
+
+ fallback:
+
+  if (!g_file_copy (source, destination, G_FILE_COPY_OVERWRITE | G_FILE_COPY_ALL_METADATA, cancellable,
+                   progress_callback, progress_callback_data,
+                   error))
+    return FALSE;
+  
+  return g_file_delete (source, cancellable, error);
+}
+
+
+static GDirectoryMonitor*
+g_local_file_monitor_dir (GFile* file,
+                         GFileMonitorFlags flags,
+                         GCancellable *cancellable)
+{
+  GLocalFile* local_file = G_LOCAL_FILE(file);
+  return g_local_directory_monitor_new (local_file->filename, flags);
+}
+
+static GFileMonitor*
+g_local_file_monitor_file (GFile* file,
+                          GFileMonitorFlags flags,
+                          GCancellable *cancellable)
+{
+  GLocalFile* local_file = G_LOCAL_FILE(file);
+  return g_local_file_monitor_new (local_file->filename, flags);
+}
+
+static void
+g_local_file_file_iface_init (GFileIface *iface)
+{
+  iface->dup = g_local_file_dup;
+  iface->hash = g_local_file_hash;
+  iface->equal = g_local_file_equal;
+  iface->is_native = g_local_file_is_native;
+  iface->has_uri_scheme = g_local_file_has_uri_scheme;
+  iface->get_uri_scheme = g_local_file_get_uri_scheme;
+  iface->get_basename = g_local_file_get_basename;
+  iface->get_path = g_local_file_get_path;
+  iface->get_uri = g_local_file_get_uri;
+  iface->get_parse_name = g_local_file_get_parse_name;
+  iface->get_parent = g_local_file_get_parent;
+  iface->contains_file = g_local_file_contains_file;
+  iface->get_relative_path = g_local_file_get_relative_path;
+  iface->resolve_relative_path = g_local_file_resolve_relative_path;
+  iface->get_child_for_display_name = g_local_file_get_child_for_display_name;
+  iface->set_display_name = g_local_file_set_display_name;
+  iface->enumerate_children = g_local_file_enumerate_children;
+  iface->query_info = g_local_file_query_info;
+  iface->query_filesystem_info = g_local_file_query_filesystem_info;
+  iface->find_enclosing_volume = g_local_file_find_enclosing_volume;
+  iface->query_settable_attributes = g_local_file_query_settable_attributes;
+  iface->query_writable_namespaces = g_local_file_query_writable_namespaces;
+  iface->set_attribute = g_local_file_set_attribute;
+  iface->set_attributes_from_info = g_local_file_set_attributes_from_info;
+  iface->read = g_local_file_read;
+  iface->append_to = g_local_file_append_to;
+  iface->create = g_local_file_create;
+  iface->replace = g_local_file_replace;
+  iface->delete_file = g_local_file_delete;
+  iface->trash = g_local_file_trash;
+  iface->make_directory = g_local_file_make_directory;
+  iface->make_symbolic_link = g_local_file_make_symbolic_link;
+  iface->copy = g_local_file_copy;
+  iface->move = g_local_file_move;
+  iface->monitor_dir = g_local_file_monitor_dir;
+  iface->monitor_file = g_local_file_monitor_file;
+}
diff --git a/gio/glocalfile.h b/gio/glocalfile.h
new file mode 100644 (file)
index 0000000..62e22f6
--- /dev/null
@@ -0,0 +1,51 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_FILE_H__
+#define __G_LOCAL_FILE_H__
+
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_FILE         (g_local_file_get_type ())
+#define G_LOCAL_FILE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_FILE, GLocalFile))
+#define G_LOCAL_FILE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LOCAL_FILE, GLocalFileClass))
+#define G_IS_LOCAL_FILE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_FILE))
+#define G_IS_LOCAL_FILE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_FILE))
+#define G_LOCAL_FILE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LOCAL_FILE, GLocalFileClass))
+
+typedef struct _GLocalFile        GLocalFile;
+typedef struct _GLocalFileClass   GLocalFileClass;
+
+struct _GLocalFileClass
+{
+  GObjectClass parent_class;
+};
+
+GType g_local_file_get_type (void) G_GNUC_CONST;
+  
+GFile * g_local_file_new (const char *filename);
+
+G_END_DECLS
+
+#endif /* __G_LOCAL_FILE_H__ */
diff --git a/gio/glocalfileenumerator.c b/gio/glocalfileenumerator.c
new file mode 100644 (file)
index 0000000..8548fcf
--- /dev/null
@@ -0,0 +1,228 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glocalfileenumerator.h>
+#include <glocalfileinfo.h>
+#include "glibintl.h"
+
+  /* TODO:
+   *  It would be nice to use the dirent->d_type to check file type without
+   *  needing to stat each files on linux and other systems that support it.
+   *  (question: does that following symlink or not?)
+   */
+  
+
+struct _GLocalFileEnumerator
+{
+  GFileEnumerator parent;
+
+  GFileAttributeMatcher *matcher;
+  GDir *dir;
+  char *filename;
+  char *attributes;
+  GFileQueryInfoFlags flags;
+
+  gboolean got_parent_info;
+  GLocalParentFileInfo parent_info;
+  
+  gboolean follow_symlinks;
+};
+
+G_DEFINE_TYPE (GLocalFileEnumerator, g_local_file_enumerator, G_TYPE_FILE_ENUMERATOR);
+
+static GFileInfo *g_local_file_enumerator_next_file (GFileEnumerator  *enumerator,
+                                                    GCancellable     *cancellable,
+                                                    GError          **error);
+static gboolean   g_local_file_enumerator_close     (GFileEnumerator  *enumerator,
+                                                    GCancellable     *cancellable,
+                                                    GError          **error);
+
+
+static void
+g_local_file_enumerator_finalize (GObject *object)
+{
+  GLocalFileEnumerator *local;
+
+  local = G_LOCAL_FILE_ENUMERATOR (object);
+
+  g_free (local->filename);
+  g_file_attribute_matcher_unref (local->matcher);
+  if (local->dir)
+    {
+      g_dir_close (local->dir);
+      local->dir = NULL;
+    }
+  
+  if (G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_local_file_enumerator_parent_class)->finalize) (object);
+}
+
+
+static void
+g_local_file_enumerator_class_init (GLocalFileEnumeratorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GFileEnumeratorClass *enumerator_class = G_FILE_ENUMERATOR_CLASS (klass);
+  
+  gobject_class->finalize = g_local_file_enumerator_finalize;
+
+  enumerator_class->next_file = g_local_file_enumerator_next_file;
+  enumerator_class->close = g_local_file_enumerator_close;
+}
+
+static void
+g_local_file_enumerator_init (GLocalFileEnumerator *local)
+{
+}
+
+static void
+convert_file_to_io_error (GError **error,
+                         GError *file_error)
+{
+  int new_code;
+
+  if (file_error == NULL)
+    return;
+  
+  new_code = G_IO_ERROR_FAILED;
+  
+  if (file_error->domain == G_FILE_ERROR) {
+    switch (file_error->code) {
+    case G_FILE_ERROR_NOENT:
+      new_code = G_IO_ERROR_NOT_FOUND;
+      break;
+    case G_FILE_ERROR_ACCES:
+      new_code = G_IO_ERROR_PERMISSION_DENIED;
+      break;
+    case G_FILE_ERROR_NOTDIR:
+      new_code = G_IO_ERROR_NOT_DIRECTORY;
+      break;
+    default:
+      break;
+    }
+  }
+  
+  g_set_error (error, G_IO_ERROR,
+              new_code,
+              "%s", file_error->message);
+}
+
+GFileEnumerator *
+g_local_file_enumerator_new (const char *filename,
+                            const char *attributes,
+                            GFileQueryInfoFlags flags,
+                            GCancellable *cancellable,
+                            GError **error)
+{
+  GLocalFileEnumerator *local;
+  GDir *dir;
+  GError *dir_error;
+  int new_code;
+
+  dir_error = NULL;
+  dir = g_dir_open (filename, 0, error != NULL ? &dir_error : NULL);
+  if (dir == NULL) {
+    convert_file_to_io_error (error, dir_error);
+    g_error_free (dir_error);
+    return NULL;
+  }
+  
+  local = g_object_new (G_TYPE_LOCAL_FILE_ENUMERATOR, NULL);
+
+  local->dir = dir;
+  local->filename = g_strdup (filename);
+  local->matcher = g_file_attribute_matcher_new (attributes);
+  local->flags = flags;
+  
+  return G_FILE_ENUMERATOR (local);
+}
+
+static GFileInfo *
+g_local_file_enumerator_next_file (GFileEnumerator *enumerator,
+                                  GCancellable     *cancellable,
+                                  GError **error)
+{
+  GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
+  const char *filename;
+  char *path;
+  GFileInfo *info;
+  GError *my_error = NULL;
+
+  if (!local->got_parent_info)
+    {
+      _g_local_file_info_get_parent_info (local->filename, local->matcher, &local->parent_info);
+      local->got_parent_info = TRUE;
+    }
+  
+ next_file:
+  
+  filename = g_dir_read_name (local->dir);
+  if (filename == NULL)
+    return NULL;
+
+  path = g_build_filename (local->filename, filename, NULL);
+  info = _g_local_file_info_get (filename, path,
+                                local->matcher,
+                                local->flags,
+                                &local->parent_info,
+                                &my_error); 
+  g_free (path);
+  
+  if (info == NULL)
+    {
+      /* Failed to get info */
+      /* If the file does not exist there might have been a race where
+       * the file was removed between the readdir and the stat, so we
+       * ignore the file. */
+      if (my_error->domain == G_IO_ERROR &&
+         my_error->code == G_IO_ERROR_NOT_FOUND)
+       {
+         g_error_free (my_error);
+         goto next_file;
+       }
+      else
+       g_propagate_error (error, my_error);
+    }
+
+  return info;
+}
+
+static gboolean
+g_local_file_enumerator_close (GFileEnumerator *enumerator,
+                              GCancellable     *cancellable,
+                              GError          **error)
+{
+  GLocalFileEnumerator *local = G_LOCAL_FILE_ENUMERATOR (enumerator);
+
+  if (local->dir)
+    {
+      g_dir_close (local->dir);
+      local->dir = NULL;
+    }
+
+  return TRUE;
+}
+
+
diff --git a/gio/glocalfileenumerator.h b/gio/glocalfileenumerator.h
new file mode 100644 (file)
index 0000000..67b49ed
--- /dev/null
@@ -0,0 +1,60 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_FILE_ENUMERATOR_H__
+#define __G_LOCAL_FILE_ENUMERATOR_H__
+
+#include <gfileenumerator.h>
+#include <gfileinfo.h>
+#include <gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_FILE_ENUMERATOR         (g_local_file_enumerator_get_type ())
+#define G_LOCAL_FILE_ENUMERATOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_FILE_ENUMERATOR, GLocalFileEnumerator))
+#define G_LOCAL_FILE_ENUMERATOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LOCAL_FILE_ENUMERATOR, GLocalFileEnumeratorClass))
+#define G_IS_LOCAL_FILE_ENUMERATOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_FILE_ENUMERATOR))
+#define G_IS_LOCAL_FILE_ENUMERATOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_FILE_ENUMERATOR))
+#define G_LOCAL_FILE_ENUMERATOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LOCAL_FILE_ENUMERATOR, GLocalFileEnumeratorClass))
+
+typedef struct _GLocalFileEnumerator         GLocalFileEnumerator;
+typedef struct _GLocalFileEnumeratorClass    GLocalFileEnumeratorClass;
+typedef struct _GLocalFileEnumeratorPrivate  GLocalFileEnumeratorPrivate;
+
+
+struct _GLocalFileEnumeratorClass
+{
+  GFileEnumeratorClass parent_class;
+
+};
+
+GType g_local_file_enumerator_get_type (void) G_GNUC_CONST;
+
+GFileEnumerator *g_local_file_enumerator_new (const char *filename,
+                                             const char *attributes,
+                                             GFileQueryInfoFlags flags,
+                                             GCancellable *cancellable,
+                                             GError **error);
+
+G_END_DECLS
+
+#endif /* __G_FILE_LOCAL_FILE_ENUMERATOR_H__ */
diff --git a/gio/glocalfileinfo.c b/gio/glocalfileinfo.c
new file mode 100644 (file)
index 0000000..5f6176b
--- /dev/null
@@ -0,0 +1,2216 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+#ifdef HAVE_SELINUX
+#include <selinux/selinux.h>
+#endif
+
+#ifdef HAVE_XATTR
+
+#if defined HAVE_SYS_XATTR_H
+  #include <sys/xattr.h>
+#elif defined HAVE_ATTR_XATTR_H
+  #include <attr/xattr.h>
+#else
+  #error "Neither <sys/xattr.h> nor <attr/xattr.h> is present but extended attribute support is enabled."
+#endif /* defined HAVE_SYS_XATTR_H || HAVE_ATTR_XATTR_H */
+
+#endif /* HAVE_XATTR */
+
+#include <glib/gstdio.h>
+#include "glibintl.h"
+
+#include "glocalfileinfo.h"
+#include "gioerror.h"
+#include "gthemedicon.h"
+#include "gcontenttype.h"
+#include "gcontenttypeprivate.h"
+
+struct ThumbMD5Context {
+       guint32 buf[4];
+       guint32 bits[2];
+       unsigned char in[64];
+};
+
+typedef struct {
+  char *user_name;
+  char *real_name;
+} UidData;
+
+G_LOCK_DEFINE_STATIC (uid_cache);
+static GHashTable *uid_cache = NULL;
+
+G_LOCK_DEFINE_STATIC (gid_cache);
+static GHashTable *gid_cache = NULL;
+
+static void thumb_md5 (const char *string, unsigned char digest[16]);
+
+char *
+_g_local_file_info_create_etag (struct stat *statbuf)
+{
+  GTimeVal tv;
+  
+  tv.tv_sec = statbuf->st_mtime;
+#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
+  tv.tv_usec = statbuf->st_mtimensec / 1000;
+#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
+  tv.tv_usec = statbuf->st_mtim.tv_nsec / 1000;
+#else
+  tv.tv_usec = 0;
+#endif
+
+  return g_strdup_printf ("%lu:%lu", tv.tv_sec, tv.tv_usec);
+}
+
+static char *
+_g_local_file_info_create_file_id (struct stat *statbuf)
+{
+  return g_strdup_printf ("l%" G_GUINT64_FORMAT ":%" G_GUINT64_FORMAT,
+                         (guint64) statbuf->st_dev, 
+                         (guint64) statbuf->st_ino);
+}
+
+static char *
+_g_local_file_info_create_fs_id (struct stat *statbuf)
+{
+  return g_strdup_printf ("l%" G_GUINT64_FORMAT,
+                         (guint64) statbuf->st_dev);
+}
+
+
+static gchar *
+read_link (const gchar *full_name)
+{
+#ifdef HAVE_READLINK
+  gchar *buffer;
+  guint size;
+  
+  size = 256;
+  buffer = g_malloc (size);
+  
+  while (1)
+    {
+      int read_size;
+      
+      read_size = readlink (full_name, buffer, size);
+      if (read_size < 0)
+       {
+         g_free (buffer);
+         return NULL;
+       }
+      if (read_size < size)
+       {
+         buffer[read_size] = 0;
+         return buffer;
+       }
+      size *= 2;
+      buffer = g_realloc (buffer, size);
+    }
+#else
+  return NULL;
+#endif
+}
+
+/* Get the SELinux security context */
+static void
+get_selinux_context (const char *path,
+                    GFileInfo *info,
+                    GFileAttributeMatcher *attribute_matcher,
+                    gboolean follow_symlinks)
+{
+#ifdef HAVE_SELINUX
+  char *context;
+
+  if (!g_file_attribute_matcher_matches (attribute_matcher, "selinux:context"))
+    return;
+  
+  if (is_selinux_enabled ())
+    {
+      if (follow_symlinks)
+       {
+         if (lgetfilecon_raw (path, &context) < 0)
+           return;
+       }
+      else
+       {
+         if (getfilecon_raw (path, &context) < 0)
+           return;
+       }
+
+      if (context)
+       {
+         g_file_info_set_attribute_string (info, "selinux:context", context);
+         freecon(context);
+       }
+    }
+#endif
+}
+
+#ifdef HAVE_XATTR
+
+static gboolean
+valid_char (char c)
+{
+  return c >= 32 && c <= 126 && c != '\\';
+}
+
+static gboolean
+name_is_valid (const char *str)
+{
+  while (*str)
+    {
+      if (!valid_char (*str++))
+       return FALSE;
+    }
+  return TRUE;
+}
+
+static char *
+hex_escape_string (const char *str, gboolean *free_return)
+{
+  int num_invalid, i;
+  char *escaped_str, *p;
+  unsigned char c;
+  static char *hex_digits = "0123456789abcdef";
+  int len;
+
+  len = strlen (str);
+  
+  num_invalid = 0;
+  for (i = 0; i < len; i++)
+    {
+      if (!valid_char (str[i]))
+       num_invalid++;
+    }
+
+  if (num_invalid == 0)
+    {
+      *free_return = FALSE;
+      return (char *)str;
+    }
+
+  escaped_str = g_malloc (len + num_invalid*3 + 1);
+
+  p = escaped_str;
+  for (i = 0; i < len; i++)
+    {
+      if (valid_char (str[i]))
+       *p++ = str[i];
+      else
+       {
+         c = str[i];
+         *p++ = '\\';
+         *p++ = 'x';
+         *p++ = hex_digits[(c >> 4) & 0xf];
+         *p++ = hex_digits[c & 0xf];
+       }
+    }
+  *p++ = 0;
+
+  *free_return = TRUE;
+  return escaped_str;
+}
+
+static char *
+hex_unescape_string (const char *str, int *out_len, gboolean *free_return)
+{
+  int i;
+  char *unescaped_str, *p;
+  unsigned char c;
+  int len;
+
+  len = strlen (str);
+  
+  if (strchr (str, '\\') == NULL)
+    {
+      if (out_len)
+       *out_len = len;
+      *free_return = FALSE;
+      return (char *)str;
+    }
+  
+  unescaped_str = g_malloc (len + 1);
+
+  p = unescaped_str;
+  for (i = 0; i < len; i++)
+    {
+      if (str[i] == '\\' &&
+         str[i+1] == 'x' &&
+         len - i >= 4)
+       {
+         c =
+           (g_ascii_xdigit_value (str[i+2]) << 4) |
+           g_ascii_xdigit_value (str[i+3]);
+         *p++ = c;
+         i += 3;
+       }
+      else
+       *p++ = str[i];
+    }
+  *p++ = 0;
+
+  if (out_len)
+    *out_len = p - unescaped_str;
+  *free_return = TRUE;
+  return unescaped_str;
+}
+
+static void
+escape_xattr (GFileInfo *info,
+             const char *gio_attr, /* gio attribute name */
+             const char *value, /* Is zero terminated */
+             size_t len /* not including zero termination */)
+{
+  char *escaped_val;
+  gboolean free_escaped_val;
+  
+  escaped_val = hex_escape_string (value, &free_escaped_val);
+  
+  g_file_info_set_attribute_string (info, gio_attr, escaped_val);
+  
+  if (free_escaped_val)
+    g_free (escaped_val);
+}
+
+static void
+get_one_xattr (const char *path,
+              GFileInfo *info,
+              const char *gio_attr,
+              const char *xattr,
+              gboolean follow_symlinks)
+{
+  char value[64];
+  char *value_p;
+  ssize_t len;
+
+  if (follow_symlinks)  
+    len = getxattr (path, xattr, value, sizeof (value)-1);
+  else
+    len = lgetxattr (path, xattr,value, sizeof (value)-1);
+
+  value_p = NULL;
+  if (len >= 0)
+    value_p = value;
+  else if (len == -1 && errno == ERANGE)
+    {
+      if (follow_symlinks)  
+       len = getxattr (path, xattr, NULL, 0);
+      else
+       len = lgetxattr (path, xattr, NULL, 0);
+
+      if (len < 0)
+       return;
+
+      value_p = g_malloc (len+1);
+
+      if (follow_symlinks)  
+       len = getxattr (path, xattr, value_p, len);
+      else
+       len = lgetxattr (path, xattr, value_p, len);
+
+      if (len < 0)
+       {
+         g_free (value_p);
+         return;
+       }
+    }
+  else
+    return;
+  
+  /* Null terminate */
+  value_p[len] = 0;
+
+  escape_xattr (info, gio_attr, value_p, len);
+  
+  if (value_p != value)
+    g_free (value_p);
+}
+
+#endif /* defined HAVE_XATTR */
+
+static void
+get_xattrs (const char *path,
+           gboolean user,
+           GFileInfo *info,
+           GFileAttributeMatcher *matcher,
+           gboolean follow_symlinks)
+{
+#ifdef HAVE_XATTR
+  gboolean all;
+  gsize list_size;
+  ssize_t list_res_size;
+  size_t len;
+  char *list;
+  const char *attr, *attr2;
+
+  if (user)
+    all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
+  else
+    all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr_sys");
+
+  if (all)
+    {
+      if (follow_symlinks)
+       list_res_size = listxattr (path, NULL, 0);
+      else
+       list_res_size = llistxattr (path, NULL, 0);
+
+      if (list_res_size == -1 ||
+         list_res_size == 0)
+       return;
+
+      list_size = list_res_size;
+      list = g_malloc (list_size);
+
+    retry:
+      
+      if (follow_symlinks)
+       list_res_size = listxattr (path, list, list_size);
+      else
+       list_res_size = llistxattr (path, list, list_size);
+      
+      if (list_res_size == -1 && errno == ERANGE)
+       {
+         list_size = list_size * 2;
+         list = g_realloc (list, list_size);
+         goto retry;
+       }
+
+      if (list_res_size == -1)
+       return;
+
+      attr = list;
+      while (list_res_size > 0)
+       {
+         if ((user && g_str_has_prefix (attr, "user.")) ||
+             (!user && !g_str_has_prefix (attr, "user.")))
+           {
+             char *escaped_attr, *gio_attr;
+             gboolean free_escaped_attr;
+             
+             if (user)
+               {
+                 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
+                 gio_attr = g_strconcat ("xattr:", escaped_attr, NULL);
+               }
+             else
+               {
+                 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
+                 gio_attr = g_strconcat ("xattr_sys:", escaped_attr, NULL);
+               }
+             
+             if (free_escaped_attr)
+               g_free (escaped_attr);
+             
+             get_one_xattr (path, info, gio_attr, attr, follow_symlinks);
+           }
+             
+         len = strlen (attr) + 1;
+         attr += len;
+         list_res_size -= len;
+       }
+
+      g_free (list);
+    }
+  else
+    {
+      while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
+       {
+         char *unescaped_attribute, *a;
+         gboolean free_unescaped_attribute;
+
+         attr2 = strchr (attr, ':');
+         if (attr2)
+           {
+             attr2++; /* Skip ':' */
+             unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
+             if (user)
+               a = g_strconcat ("user.", unescaped_attribute, NULL);
+             else
+               a = unescaped_attribute;
+             
+             get_one_xattr (path, info, attr, a, follow_symlinks);
+
+             if (user)
+               g_free (a);
+             
+             if (free_unescaped_attribute)
+               g_free (unescaped_attribute);
+           }
+       }
+    }
+#endif /* defined HAVE_XATTR */
+}
+
+#ifdef HAVE_XATTR
+static void
+get_one_xattr_from_fd (int fd,
+                      GFileInfo *info,
+                      const char *gio_attr,
+                      const char *xattr)
+{
+  char value[64];
+  char *value_p;
+  ssize_t len;
+
+  len = fgetxattr (fd, xattr, value, sizeof (value)-1);
+
+  value_p = NULL;
+  if (len >= 0)
+    value_p = value;
+  else if (len == -1 && errno == ERANGE)
+    {
+      len = fgetxattr (fd, xattr, NULL, 0);
+
+      if (len < 0)
+       return;
+
+      value_p = g_malloc (len+1);
+
+      len = fgetxattr (fd, xattr, value_p, len);
+
+      if (len < 0)
+       {
+         g_free (value_p);
+         return;
+       }
+    }
+  else
+    return;
+  
+  /* Null terminate */
+  value_p[len] = 0;
+
+  escape_xattr (info, gio_attr, value_p, len);
+  
+  if (value_p != value)
+    g_free (value_p);
+}
+#endif /* defined HAVE_XATTR */
+
+static void
+get_xattrs_from_fd (int fd,
+                   gboolean user,
+                   GFileInfo *info,
+                   GFileAttributeMatcher *matcher)
+{
+#ifdef HAVE_XATTR
+  gboolean all;
+  gsize list_size;
+  ssize_t list_res_size;
+  size_t len;
+  char *list;
+  const char *attr, *attr2;
+
+  if (user)
+    all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr");
+  else
+    all = g_file_attribute_matcher_enumerate_namespace (matcher, "xattr_sys");
+
+  if (all)
+    {
+      list_res_size = flistxattr (fd, NULL, 0);
+
+      if (list_res_size == -1 ||
+         list_res_size == 0)
+       return;
+
+      list_size = list_res_size;
+      list = g_malloc (list_size);
+
+    retry:
+      
+      list_res_size = flistxattr (fd, list, list_size);
+      
+      if (list_res_size == -1 && errno == ERANGE)
+       {
+         list_size = list_size * 2;
+         list = g_realloc (list, list_size);
+         goto retry;
+       }
+
+      if (list_res_size == -1)
+       return;
+
+      attr = list;
+      while (list_res_size > 0)
+       {
+         if ((user && g_str_has_prefix (attr, "user.")) ||
+             (!user && !g_str_has_prefix (attr, "user.")))
+           {
+             char *escaped_attr, *gio_attr;
+             gboolean free_escaped_attr;
+             
+             if (user)
+               {
+                 escaped_attr = hex_escape_string (attr + 5, &free_escaped_attr);
+                 gio_attr = g_strconcat ("xattr:", escaped_attr, NULL);
+               }
+             else
+               {
+                 escaped_attr = hex_escape_string (attr, &free_escaped_attr);
+                 gio_attr = g_strconcat ("xattr_sys:", escaped_attr, NULL);
+               }
+             
+             if (free_escaped_attr)
+               g_free (escaped_attr);
+             
+             get_one_xattr_from_fd (fd, info, gio_attr, attr);
+           }
+         
+         len = strlen (attr) + 1;
+         attr += len;
+         list_res_size -= len;
+       }
+
+      g_free (list);
+    }
+  else
+    {
+      while ((attr = g_file_attribute_matcher_enumerate_next (matcher)) != NULL)
+       {
+         char *unescaped_attribute, *a;
+         gboolean free_unescaped_attribute;
+
+         attr2 = strchr (attr, ':');
+         if (attr2)
+           {
+             attr2++; /* Skip ':' */
+             unescaped_attribute = hex_unescape_string (attr2, NULL, &free_unescaped_attribute);
+             if (user)
+               a = g_strconcat ("user.", unescaped_attribute, NULL);
+             else
+               a = unescaped_attribute;
+             
+             get_one_xattr_from_fd (fd, info, attr, a);
+
+             if (user)
+               g_free (a);
+             
+             if (free_unescaped_attribute)
+               g_free (unescaped_attribute);
+           }
+       }
+    }
+#endif /* defined HAVE_XATTR */
+}
+
+#ifdef HAVE_XATTR
+static gboolean
+set_xattr (char *filename,
+          const char *escaped_attribute,
+          const GFileAttributeValue *attr_value,
+          GError **error)
+{
+  char *attribute, *value;
+  gboolean free_attribute, free_value;
+  int val_len, res, errsv;
+  gboolean is_user;
+  char *a;
+
+  if (attr_value == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Attribute value must be non-NULL"));
+      return FALSE;
+    }
+
+  if (attr_value->type != G_FILE_ATTRIBUTE_TYPE_STRING)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                   _("Invalid attribute type (string expected)"));
+      return FALSE;
+    }
+
+  if (!name_is_valid (escaped_attribute))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Invalid extended attribute name"));
+      return FALSE;
+    }
+
+  if (g_str_has_prefix (escaped_attribute, "xattr:"))
+    {
+      escaped_attribute += 6;
+      is_user = TRUE;
+    }
+  else
+    {
+      g_assert (g_str_has_prefix (escaped_attribute, "xattr_sys:"));
+      escaped_attribute += 10;
+      is_user = FALSE;
+    }
+  
+  attribute = hex_unescape_string (escaped_attribute, NULL, &free_attribute);
+  value = hex_unescape_string (attr_value->u.string, &val_len, &free_value);
+
+
+  if (is_user)
+    a = g_strconcat ("user.", attribute, NULL);
+  else
+    a = attribute;
+  
+  res = setxattr (filename, a, value, val_len, 0);
+  errsv = errno;
+  
+  if (is_user)
+    g_free (a);
+  
+  if (free_attribute)
+    g_free (attribute);
+  
+  if (free_value)
+    g_free (value);
+
+  if (res == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errsv),
+                  _("Error setting extended attribute '%s': %s"),
+                  escaped_attribute, g_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+#endif
+
+
+void
+_g_local_file_info_get_parent_info (const char             *dir,
+                                   GFileAttributeMatcher  *attribute_matcher,
+                                   GLocalParentFileInfo   *parent_info)
+{
+  struct stat statbuf;
+  int res;
+  
+  parent_info->writable = FALSE;
+  parent_info->is_sticky = FALSE;
+  parent_info->device = 0;
+
+  if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME) ||
+      g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE) ||
+      g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH) ||
+      g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT))
+    {
+      parent_info->writable = (g_access (dir, W_OK) == 0);
+      
+      res = g_stat (dir, &statbuf);
+
+      /*
+       * The sticky bit (S_ISVTX) on a directory means that a file in that directory can be
+       * renamed or deleted only by the owner of the file, by the owner of the directory, and
+       * by a privileged process.
+       */
+      if (res == 0)
+       {
+         parent_info->is_sticky = (statbuf.st_mode & S_ISVTX) != 0;
+         parent_info->owner = statbuf.st_uid;
+         parent_info->device = statbuf.st_dev;
+       }
+    }
+}
+
+static void
+get_access_rights (GFileAttributeMatcher *attribute_matcher,
+                  GFileInfo *info,
+                  const gchar *path,
+                  struct stat *statbuf,
+                  GLocalParentFileInfo *parent_info)
+{
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_ACCESS_CAN_READ))
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_READ,
+                                      g_access (path, R_OK) == 0);
+  
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE))
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_WRITE,
+                                      g_access (path, W_OK) == 0);
+  
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE))
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE,
+                                      g_access (path, X_OK) == 0);
+
+
+  if (parent_info)
+    {
+      gboolean writable;
+
+      writable = FALSE;
+      if (parent_info->writable)
+       {
+         if (parent_info->is_sticky)
+           {
+             uid_t uid = geteuid ();
+
+             if (uid == statbuf->st_uid ||
+                 uid == parent_info->owner ||
+                 uid == 0)
+               writable = TRUE;
+           }
+         else
+           writable = TRUE;
+       }
+
+      if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME))
+       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_RENAME,
+                                          writable);
+      
+      if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE))
+       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_DELETE,
+                                          writable);
+
+      /* TODO: This means we can move it, but we should also look for a trash dir */
+      if (g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH))
+       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_ACCESS_CAN_TRASH,
+                                          writable);
+    }
+}
+
+static void
+set_info_from_stat (GFileInfo *info, struct stat *statbuf,
+                   GFileAttributeMatcher *attribute_matcher)
+{
+  GFileType file_type;
+
+  file_type = G_FILE_TYPE_UNKNOWN;
+
+  if (S_ISREG (statbuf->st_mode))
+    file_type = G_FILE_TYPE_REGULAR;
+  else if (S_ISDIR (statbuf->st_mode))
+    file_type = G_FILE_TYPE_DIRECTORY;
+  else if (S_ISCHR (statbuf->st_mode) ||
+          S_ISBLK (statbuf->st_mode) ||
+          S_ISFIFO (statbuf->st_mode)
+#ifdef S_ISSOCK
+          || S_ISSOCK (statbuf->st_mode)
+#endif
+          )
+    file_type = G_FILE_TYPE_SPECIAL;
+#ifdef S_ISLNK
+  else if (S_ISLNK (statbuf->st_mode))
+    file_type = G_FILE_TYPE_SYMBOLIC_LINK;
+#endif
+
+  g_file_info_set_file_type (info, file_type);
+  g_file_info_set_size (info, statbuf->st_size);
+
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_DEVICE, statbuf->st_dev);
+  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_INODE, statbuf->st_ino);
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_MODE, statbuf->st_mode);
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_NLINK, statbuf->st_nlink);
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_UID, statbuf->st_uid);
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_GID, statbuf->st_gid);
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_RDEV, statbuf->st_rdev);
+#if defined (HAVE_STRUCT_STAT_BLKSIZE)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_UNIX_BLOCK_SIZE, statbuf->st_blksize);
+#endif
+#if defined (HAVE_STRUCT_STAT_BLOCKS)
+  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_UNIX_BLOCKS, statbuf->st_blocks);
+#endif
+  
+  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED, statbuf->st_mtime);
+#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, statbuf->st_mtimensec / 1000);
+#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC, statbuf->st_mtim.tv_nsec / 1000);
+#endif
+  
+  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_ACCESS, statbuf->st_atime);
+#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atimensec / 1000);
+#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC, statbuf->st_atim.tv_nsec / 1000);
+#endif
+  
+  g_file_info_set_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_CHANGED, statbuf->st_ctime);
+#if defined (HAVE_STRUCT_STAT_ST_CTIMENSEC)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctimensec / 1000);
+#elif defined (HAVE_STRUCT_STAT_ST_CTIM_TV_NSEC)
+  g_file_info_set_attribute_uint32 (info, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, statbuf->st_ctim.tv_nsec / 1000);
+#endif
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_ETAG_VALUE))
+    {
+      char *etag = _g_local_file_info_create_etag (statbuf);
+      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ETAG_VALUE, etag);
+      g_free (etag);
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_ID_FILE))
+    {
+      char *id = _g_local_file_info_create_file_id (statbuf);
+      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FILE, id);
+      g_free (id);
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_ID_FS))
+    {
+      char *id = _g_local_file_info_create_fs_id (statbuf);
+      g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_ID_FS, id);
+      g_free (id);
+    }
+}
+
+static char *
+make_valid_utf8 (const char *name)
+{
+  GString *string;
+  const gchar *remainder, *invalid;
+  gint remaining_bytes, valid_bytes;
+  
+  string = NULL;
+  remainder = name;
+  remaining_bytes = strlen (name);
+  
+  while (remaining_bytes != 0) 
+    {
+      if (g_utf8_validate (remainder, remaining_bytes, &invalid)) 
+       break;
+      valid_bytes = invalid - remainder;
+    
+      if (string == NULL) 
+       string = g_string_sized_new (remaining_bytes);
+
+      g_string_append_len (string, remainder, valid_bytes);
+      /* append U+FFFD REPLACEMENT CHARACTER */
+      g_string_append (string, "\357\277\275");
+      
+      remaining_bytes -= valid_bytes + 1;
+      remainder = invalid + 1;
+    }
+  
+  if (string == NULL)
+    return g_strdup (name);
+  
+  g_string_append (string, remainder);
+
+  g_assert (g_utf8_validate (string->str, -1, NULL));
+  
+  return g_string_free (string, FALSE);
+}
+
+static char *
+convert_pwd_string_to_utf8 (char *pwd_str)
+{
+  char *utf8_string;
+  
+  if (!g_utf8_validate (pwd_str, -1, NULL))
+    {
+      utf8_string = g_locale_to_utf8 (pwd_str, -1, NULL, NULL, NULL);
+      if (utf8_string == NULL)
+       utf8_string = make_valid_utf8 (pwd_str);
+    }
+  else 
+    utf8_string = g_strdup (pwd_str);
+  
+  return utf8_string;
+}
+
+static void
+uid_data_free (UidData *data)
+{
+  g_free (data->user_name);
+  g_free (data->real_name);
+  g_free (data);
+}
+
+/* called with lock held */
+static UidData *
+lookup_uid_data (uid_t uid)
+{
+  UidData *data;
+  char buffer[4096];
+  struct passwd pwbuf;
+  struct passwd *pwbufp;
+  char *gecos, *comma;
+  
+  if (uid_cache == NULL)
+    uid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)uid_data_free);
+
+  data = g_hash_table_lookup (uid_cache, GINT_TO_POINTER (uid));
+
+  if (data)
+    return data;
+
+  data = g_new0 (UidData, 1);
+
+#if defined(HAVE_POSIX_GETPWUID_R)
+  getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer), &pwbufp);
+#elif defined(HAVE_NONPOSIX_GETPWUID_R)
+  pwbufp = getpwuid_r (uid, &pwbuf, buffer, sizeof(buffer));
+#else
+  pwbufp = getpwuid (uid);
+#endif
+
+  if (pwbufp != NULL)
+    {
+      if (pwbufp->pw_name != NULL && pwbufp->pw_name[0] != 0)
+       data->user_name = convert_pwd_string_to_utf8 (pwbufp->pw_name);
+
+      gecos = pwbufp->pw_gecos;
+
+      if (gecos)
+       {
+         comma = strchr (gecos, ',');
+         if (comma)
+           *comma = 0;
+         data->real_name = convert_pwd_string_to_utf8 (gecos);
+       }
+    }
+
+  /* Default fallbacks */
+  if (data->real_name == NULL)
+    {
+      if (data->user_name != NULL)
+       data->real_name = g_strdup (data->user_name);
+      else
+       data->real_name = g_strdup_printf("user #%d", (int)uid);
+    }
+  
+  if (data->user_name == NULL)
+    data->user_name = g_strdup_printf("%d", (int)uid);
+  
+  g_hash_table_replace (uid_cache, GINT_TO_POINTER (uid), data);
+  
+  return data;
+}
+
+static char *
+get_username_from_uid (uid_t uid)
+{
+  char *res;
+  UidData *data;
+  
+  G_LOCK (uid_cache);
+  data = lookup_uid_data (uid);
+  res = g_strdup (data->user_name);  
+  G_UNLOCK (uid_cache);
+
+  return res;
+}
+
+static char *
+get_realname_from_uid (uid_t uid)
+{
+  char *res;
+  UidData *data;
+  
+  G_LOCK (uid_cache);
+  data = lookup_uid_data (uid);
+  res = g_strdup (data->real_name);  
+  G_UNLOCK (uid_cache);
+  
+  return res;
+}
+
+
+/* called with lock held */
+static char *
+lookup_gid_name (gid_t gid)
+{
+  char *name;
+  char buffer[4096];
+  struct group gbuf;
+  struct group *gbufp;
+  
+  if (gid_cache == NULL)
+    gid_cache = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify)g_free);
+
+  name = g_hash_table_lookup (gid_cache, GINT_TO_POINTER (gid));
+
+  if (name)
+    return name;
+
+#if defined (HAVE_POSIX_GETGRGID_R)
+  getgrgid_r (gid, &gbuf, buffer, sizeof(buffer), &gbufp);
+#elif defined (HAVE_NONPOSIX_GETGRGID_R)
+  gbufp = getgrgid_r (gid, &gbuf, buffer, sizeof(buffer));
+#else
+  gbufp = getgrgid (gid);
+#endif
+
+  if (gbufp != NULL &&
+      gbufp->gr_name != NULL &&
+      gbufp->gr_name[0] != 0)
+    name = convert_pwd_string_to_utf8 (gbufp->gr_name);
+  else
+    name = g_strdup_printf("%d", (int)gid);
+  
+  g_hash_table_replace (gid_cache, GINT_TO_POINTER (gid), name);
+  
+  return name;
+}
+
+static char *
+get_groupname_from_gid (gid_t gid)
+{
+  char *res;
+  char *name;
+  
+  G_LOCK (gid_cache);
+  name = lookup_gid_name (gid);
+  res = g_strdup (name);  
+  G_UNLOCK (gid_cache);
+  return res;
+}
+
+static char *
+get_content_type (const char *basename,
+                 const char *path,
+                 struct stat *statbuf,
+                 gboolean is_symlink,
+                 gboolean symlink_broken,
+                 GFileQueryInfoFlags flags,
+                 gboolean fast)
+{
+  if (is_symlink &&
+      (symlink_broken || (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)))
+    return g_strdup  ("inode/symlink");
+  else if (S_ISDIR(statbuf->st_mode))
+    return g_strdup ("inode/directory");
+  else if (S_ISCHR(statbuf->st_mode))
+    return g_strdup ("inode/chardevice");
+  else if (S_ISBLK(statbuf->st_mode))
+    return g_strdup ("inode/blockdevice");
+  else if (S_ISFIFO(statbuf->st_mode))
+    return g_strdup ("inode/fifo");
+#ifdef S_ISSOCK
+  else if (S_ISSOCK(statbuf->st_mode))
+    return g_strdup ("inode/socket");
+#endif
+  else
+    {
+      char *content_type;
+      gboolean result_uncertain;
+      
+      content_type = g_content_type_guess (basename, NULL, 0, &result_uncertain);
+      
+#ifndef G_OS_WIN32
+      if (!fast && result_uncertain && path != NULL)
+       {
+         guchar sniff_buffer[4096];
+         gsize sniff_length;
+         int fd;
+
+         sniff_length = _g_unix_content_type_get_sniff_len ();
+         if (sniff_length > 4096)
+           sniff_length = 4096;
+         
+         fd = open (path, O_RDONLY);
+         if (fd != -1)
+           {
+             ssize_t res;
+             
+             res = read (fd, sniff_buffer, sniff_length);
+             close (fd);
+             if (res > 0)
+               {
+                 g_free (content_type);
+                 content_type = g_content_type_guess (basename, sniff_buffer, res, NULL);
+               }
+           }
+       }
+#endif
+      
+      return content_type;
+    }
+  
+}
+
+static char *
+thumb_digest_to_ascii (unsigned char digest[16], const char *suffix)
+{
+  static const char hex_digits[] = "0123456789abcdef";
+  char *res;
+  int i;
+  
+  res = g_malloc (33 + strlen (suffix));
+  
+  for (i = 0; i < 16; i++) {
+    res[2*i] = hex_digits[digest[i] >> 4];
+    res[2*i+1] = hex_digits[digest[i] & 0xf];
+  }
+  
+  res[32] = 0;
+
+  strcat (res, suffix);
+  
+  return res;
+}
+
+
+static void
+get_thumbnail_attributes (const char *path,
+                         GFileInfo *info)
+{
+  char *uri;
+  unsigned char digest[16];
+  char *filename;
+  char *basename;
+
+  uri = g_filename_to_uri (path, NULL, NULL);
+  thumb_md5 (uri, digest);
+  g_free (uri);
+
+  basename = thumb_digest_to_ascii (digest, ".png");
+
+  filename = g_build_filename (g_get_home_dir(), ".thumbnails", "normal", basename, NULL);
+
+  if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+    g_file_info_set_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH, filename);
+  else
+    {
+      g_free (filename);
+      filename = g_build_filename (g_get_home_dir(), ".thumbnails", "fail", "gnome-thumbnail-factory", basename, NULL);
+
+      if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
+       g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED, TRUE);
+    }
+  g_free (basename);
+  g_free (filename);
+}
+
+
+GFileInfo *
+_g_local_file_info_get (const char *basename,
+                       const char *path,
+                       GFileAttributeMatcher *attribute_matcher,
+                       GFileQueryInfoFlags flags,
+                       GLocalParentFileInfo *parent_info,
+                       GError **error)
+{
+  GFileInfo *info;
+  struct stat statbuf;
+  struct stat statbuf2;
+  int res;
+  gboolean is_symlink, symlink_broken;
+
+  info = g_file_info_new ();
+
+  /* Make sure we don't set any unwanted attributes */
+  g_file_info_set_attribute_mask (info, attribute_matcher);
+  
+  g_file_info_set_name (info, basename);
+
+  /* Avoid stat in trivial case */
+  if (attribute_matcher == NULL)
+    return info;
+
+  res = g_lstat (path, &statbuf);
+  if (res == -1)
+    {
+      g_object_unref (info);
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error stating file '%s': %s"),
+                  path, g_strerror (errno));
+      return NULL;
+    }
+  
+#ifdef S_ISLNK
+  is_symlink = S_ISLNK (statbuf.st_mode);
+#else
+  is_symlink = FALSE;
+#endif
+  symlink_broken = FALSE;
+  
+  if (is_symlink)
+    {
+      g_file_info_set_is_symlink (info, TRUE);
+
+      /* Unless NOFOLLOW was set we default to following symlinks */
+      if (!(flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS))
+       {
+         res = stat (path, &statbuf2);
+
+           /* Report broken links as symlinks */
+         if (res != -1)
+           statbuf = statbuf2;
+         else
+           symlink_broken = TRUE;
+       }
+    }
+
+  set_info_from_stat (info, &statbuf, attribute_matcher);
+  
+  if (basename != NULL && basename[0] == '.')
+    g_file_info_set_is_hidden (info, TRUE);
+
+  if (basename != NULL && basename[strlen (basename) -1] == '~')
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_STD_IS_BACKUP, TRUE);
+
+  if (is_symlink &&
+      g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET))
+    {
+      char *link = read_link (path);
+      g_file_info_set_symlink_target (info, link);
+      g_free (link);
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_DISPLAY_NAME))
+    {
+      char *display_name = g_filename_display_basename (path);
+      
+      if (strstr (display_name, "\357\277\275") != NULL)
+       {
+         char *p = display_name;
+         display_name = g_strconcat (display_name, _(" (invalid encoding)"), NULL);
+         g_free (p);
+       }
+      g_file_info_set_display_name (info, display_name);
+      g_free (display_name);
+    }
+  
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_EDIT_NAME))
+    {
+      char *edit_name = g_filename_display_basename (path);
+      g_file_info_set_edit_name (info, edit_name);
+      g_free (edit_name);
+    }
+
+  
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_COPY_NAME))
+    {
+      char *copy_name = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
+      if (copy_name)
+       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STD_COPY_NAME, copy_name);
+      g_free (copy_name);
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_CONTENT_TYPE) ||
+      g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_ICON))
+    {
+      char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, FALSE);
+
+      if (content_type)
+       {
+         g_file_info_set_content_type (info, content_type);
+
+         if (g_file_attribute_matcher_matches (attribute_matcher,
+                                               G_FILE_ATTRIBUTE_STD_ICON))
+           {
+             char *mimetype_icon, *generic_mimetype_icon, *type_icon, *p;
+             char *icon_names[3];
+             GIcon *icon;
+             int i;
+
+             mimetype_icon = g_strdup (content_type);
+             
+             while ((p = strchr(mimetype_icon, '/')) != NULL)
+               *p = '-';
+
+             p = strchr (content_type, '/');
+             if (p == NULL)
+               p = content_type + strlen (content_type);
+
+             generic_mimetype_icon = g_malloc (p - content_type + strlen ("-x-generic") + 1);
+             memcpy (generic_mimetype_icon, content_type, p - content_type);
+             memcpy (generic_mimetype_icon + (p - content_type), "-x-generic", strlen ("-x-generic"));
+             generic_mimetype_icon[(p - content_type) + strlen ("-x-generic")] = 0;
+
+             /* TODO: Special case desktop dir? That could be expensive with xdg dirs... */
+             if (strcmp (path, g_get_home_dir ()) == 0)
+               type_icon = "user-home";
+             else if (S_ISDIR (statbuf.st_mode)) 
+               type_icon = "folder";
+             else if (statbuf.st_mode & S_IXUSR)
+               type_icon = "application-x-executable";
+             else
+               type_icon = "text-x-generic";
+
+             i = 0;
+             icon_names[i++] = mimetype_icon;
+             icon_names[i++] = generic_mimetype_icon;
+             if (strcmp (generic_mimetype_icon, type_icon) != 0 &&
+                 strcmp (mimetype_icon, type_icon) != 0) 
+               icon_names[i++] = type_icon;
+             
+             icon = g_themed_icon_new_from_names (icon_names, i);
+             g_file_info_set_icon (info, icon);
+             
+             g_object_unref (icon);
+             g_free (mimetype_icon);
+             g_free (generic_mimetype_icon);
+           }
+         
+         g_free (content_type);
+       }
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_STD_FAST_CONTENT_TYPE))
+    {
+      char *content_type = get_content_type (basename, path, &statbuf, is_symlink, symlink_broken, flags, TRUE);
+      
+      if (content_type)
+       {
+         g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_STD_FAST_CONTENT_TYPE, content_type);
+         g_free (content_type);
+       }
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_OWNER_USER))
+    {
+      char *name;
+      
+      name = get_username_from_uid (statbuf.st_uid);
+      if (name)
+       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER, name);
+      g_free (name);
+    }
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_OWNER_USER_REAL))
+    {
+      char *name;
+      
+      name = get_realname_from_uid (statbuf.st_uid);
+      if (name)
+       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_USER_REAL, name);
+      g_free (name);
+    }
+  
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_OWNER_GROUP))
+    {
+      char *name;
+      
+      name = get_groupname_from_gid (statbuf.st_gid);
+      if (name)
+       g_file_info_set_attribute_string (info, G_FILE_ATTRIBUTE_OWNER_GROUP, name);
+      g_free (name);
+    }
+
+  if (parent_info && parent_info->device != 0 &&
+      g_file_attribute_matcher_matches (attribute_matcher, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT) &&
+      statbuf.st_dev != parent_info->device) 
+    g_file_info_set_attribute_boolean (info, G_FILE_ATTRIBUTE_UNIX_IS_MOUNTPOINT, TRUE);
+  
+  get_access_rights (attribute_matcher, info, path, &statbuf, parent_info);
+  
+  get_selinux_context (path, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
+  get_xattrs (path, TRUE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
+  get_xattrs (path, FALSE, info, attribute_matcher, (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS) == 0);
+
+  if (g_file_attribute_matcher_matches (attribute_matcher,
+                                       G_FILE_ATTRIBUTE_THUMBNAIL_PATH))
+    get_thumbnail_attributes (path, info);
+  
+  g_file_info_unset_attribute_mask (info);
+
+  return info;
+}
+
+GFileInfo *
+_g_local_file_info_get_from_fd (int fd,
+                               char *attributes,
+                               GError **error)
+{
+  struct stat stat_buf;
+  GFileAttributeMatcher *matcher;
+  GFileInfo *info;
+  
+  if (fstat (fd, &stat_buf) == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error stating file descriptor: %s"),
+                  g_strerror (errno));
+      return NULL;
+    }
+
+  info = g_file_info_new ();
+
+  matcher = g_file_attribute_matcher_new (attributes);
+
+  /* Make sure we don't set any unwanted attributes */
+  g_file_info_set_attribute_mask (info, matcher);
+  
+  set_info_from_stat (info, &stat_buf, matcher);
+  
+#ifdef HAVE_SELINUX
+  if (g_file_attribute_matcher_matches (matcher, "selinux:context") &&
+      is_selinux_enabled ())
+    {
+      char *context;
+      if (fgetfilecon_raw (fd, &context) >= 0)
+       {
+         g_file_info_set_attribute_string (info, "selinux:context", context);
+         freecon(context);
+       }
+    }
+#endif
+
+  get_xattrs_from_fd (fd, TRUE, info, matcher);
+  get_xattrs_from_fd (fd, FALSE, info, matcher);
+  
+  g_file_attribute_matcher_unref (matcher);
+
+  g_file_info_unset_attribute_mask (info);
+  
+  return info;
+}
+
+static gboolean
+get_uint32 (const GFileAttributeValue *value,
+           guint32 *val_out,
+           GError **error)
+{
+  if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT32)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Invalid attribute type (uint32 expected)"));
+      return FALSE;
+    }
+
+  *val_out = value->u.uint32;
+  
+  return TRUE;
+}
+
+static gboolean
+get_uint64 (const GFileAttributeValue *value,
+           guint64 *val_out,
+           GError **error)
+{
+  if (value->type != G_FILE_ATTRIBUTE_TYPE_UINT64)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Invalid attribute type (uint64 expected)"));
+      return FALSE;
+    }
+
+  *val_out = value->u.uint64;
+  
+  return TRUE;
+}
+
+#if defined(HAVE_SYMLINK)
+static gboolean
+get_byte_string (const GFileAttributeValue *value,
+                const char **val_out,
+                GError **error)
+{
+  if (value->type != G_FILE_ATTRIBUTE_TYPE_BYTE_STRING)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Invalid attribute type (byte string expected)"));
+      return FALSE;
+    }
+
+  *val_out = value->u.string;
+  
+  return TRUE;
+}
+#endif
+
+static gboolean
+set_unix_mode (char *filename,
+              const GFileAttributeValue *value,
+              GError **error)
+{
+  guint32 val;
+  
+  if (!get_uint32 (value, &val, error))
+    return FALSE;
+  
+  if (g_chmod (filename, val) == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error setting permissions: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  return TRUE;
+}
+
+#ifdef HAVE_CHOWN
+static gboolean
+set_unix_uid_gid (char *filename,
+                 const GFileAttributeValue *uid_value,
+                 const GFileAttributeValue *gid_value,
+                 GFileQueryInfoFlags flags,
+                 GError **error)
+{
+  int res;
+  guint32 val;
+  uid_t uid;
+  gid_t gid;
+  
+  if (uid_value)
+    {
+      if (!get_uint32 (uid_value, &val, error))
+       return FALSE;
+      uid = val;
+    }
+  else
+    uid = -1;
+  
+  if (gid_value)
+    {
+      if (!get_uint32 (gid_value, &val, error))
+       return FALSE;
+      gid = val;
+    }
+  else
+    gid = -1;
+  
+  if (flags & G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS)
+    res = lchown (filename, uid, gid);
+  else
+    res = chown (filename, uid, gid);
+  
+  if (res == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error setting owner: %s"),
+                  g_strerror (errno));
+         return FALSE;
+    }
+  return TRUE;
+}
+#endif
+
+#ifdef HAVE_SYMLINK
+static gboolean
+set_symlink (char *filename,
+            const GFileAttributeValue *value,
+            GError **error)
+{
+  const char *val;
+  struct stat statbuf;
+  
+  if (!get_byte_string (value, &val, error))
+    return FALSE;
+  
+  if (val == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("symlink must be non-NULL"));
+      return FALSE;
+    }
+  
+  if (g_lstat (filename, &statbuf))
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error setting symlink: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  if (!S_ISLNK (statbuf.st_mode))
+    {
+      g_set_error (error, G_IO_ERROR,
+                  G_IO_ERROR_NOT_SYMBOLIC_LINK,
+                  _("Error setting symlink: file is not a symlink"));
+      return FALSE;
+    }
+  
+  if (g_unlink (filename))
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error setting symlink: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  if (symlink (filename, val) != 0)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error setting symlink: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+#endif
+
+static int
+lazy_stat (char *filename, struct stat *statbuf, gboolean *called_stat)
+{
+  int res;
+
+  if (*called_stat)
+    return 0;
+  
+  res = g_stat (filename, statbuf);
+  
+  if (res == 0)
+    *called_stat = TRUE;
+  
+  return res;
+}
+
+
+#ifdef HAVE_UTIMES
+static gboolean
+set_mtime_atime (char *filename,
+                const GFileAttributeValue *mtime_value,
+                const GFileAttributeValue *mtime_usec_value,
+                const GFileAttributeValue *atime_value,
+                const GFileAttributeValue *atime_usec_value,
+                GError **error)
+{
+  int res;
+  guint64 val;
+  guint32 val_usec;
+  struct stat statbuf;
+  gboolean got_stat = FALSE;
+  struct timeval times[2] = { {0, 0}, {0, 0} };
+
+  /* ATIME */
+  if (atime_value)
+    {
+      if (!get_uint64 (atime_value, &val, error))
+       return FALSE;
+      times[0].tv_sec = val;
+    }
+  else
+    {
+      if (lazy_stat (filename, &statbuf, &got_stat) == 0)
+       {
+         times[0].tv_sec = statbuf.st_mtime;
+#if defined (HAVE_STRUCT_STAT_ST_ATIMENSEC)
+         times[0].tv_usec = statbuf.st_atimensec / 1000;
+#elif defined (HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC)
+         times[0].tv_usec = statbuf.st_atim.tv_nsec / 1000;
+#endif
+       }
+    }
+  
+  if (atime_usec_value)
+    {
+      if (!get_uint32 (atime_usec_value, &val_usec, error))
+       return FALSE;
+      times[0].tv_usec = val_usec;
+    }
+
+  /* MTIME */
+  if (mtime_value)
+    {
+      if (!get_uint64 (mtime_value, &val, error))
+       return FALSE;
+      times[1].tv_sec = val;
+    }
+  else
+    {
+      if (lazy_stat (filename, &statbuf, &got_stat) == 0)
+       {
+         times[1].tv_sec = statbuf.st_mtime;
+#if defined (HAVE_STRUCT_STAT_ST_MTIMENSEC)
+         times[1].tv_usec = statbuf.st_mtimensec / 1000;
+#elif defined (HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
+         times[1].tv_usec = statbuf.st_mtim.tv_nsec / 1000;
+#endif
+       }
+    }
+  
+  if (mtime_usec_value)
+    {
+      if (!get_uint32 (mtime_usec_value, &val_usec, error))
+       return FALSE;
+      times[1].tv_usec = val_usec;
+    }
+  
+  res = utimes(filename, times);
+  if (res == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error setting owner: %s"),
+                  g_strerror (errno));
+         return FALSE;
+    }
+  return TRUE;
+}
+#endif
+
+gboolean
+_g_local_file_info_set_attribute (char *filename,
+                                 const char *attribute,
+                                 const GFileAttributeValue *value,
+                                 GFileQueryInfoFlags flags,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+  if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_MODE) == 0)
+    return set_unix_mode (filename, value, error);
+  
+#ifdef HAVE_CHOWN
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_UID) == 0)
+    return set_unix_uid_gid (filename, value, NULL, flags, error);
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_UNIX_GID) == 0)
+    return set_unix_uid_gid (filename, NULL, value, flags, error);
+#endif
+  
+#ifdef HAVE_SYMLINK
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET) == 0)
+    return set_symlink (filename, value, error);
+#endif
+
+#ifdef HAVE_UTIMES
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED) == 0)
+    return set_mtime_atime (filename, value, NULL, NULL, NULL, error);
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC) == 0)
+    return set_mtime_atime (filename, NULL, value, NULL, NULL, error);
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS) == 0)
+    return set_mtime_atime (filename, NULL, NULL, value, NULL, error);
+  else if (strcmp (attribute, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC) == 0)
+    return set_mtime_atime (filename, NULL, NULL, NULL, value, error);
+#endif
+
+#ifdef HAVE_XATTR
+  else if (g_str_has_prefix (attribute, "xattr:"))
+    return set_xattr (filename, attribute, value, error);
+  else if (g_str_has_prefix (attribute, "xattr_sys:"))
+    return set_xattr (filename, attribute, value, error);
+#endif
+  
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+              _("Setting attribute %s not supported"), attribute);
+  return FALSE;
+}
+
+gboolean
+_g_local_file_info_set_attributes  (char                       *filename,
+                                   GFileInfo                  *info,
+                                   GFileQueryInfoFlags         flags,
+                                   GCancellable               *cancellable,
+                                   GError                    **error)
+{
+  GFileAttributeValue *value, *uid, *gid;
+  GFileAttributeValue *mtime, *mtime_usec, *atime, *atime_usec;
+  GFileAttributeStatus status;
+  gboolean res;
+  
+  /* Handles setting multiple specified data in a single set, and takes care
+     of ordering restrictions when setting attributes */
+
+  res = TRUE;
+
+  /* Set symlink first, since this recreates the file */
+#ifdef HAVE_SYMLINK
+  value = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_STD_SYMLINK_TARGET);
+  if (value)
+    {
+      if (!set_symlink (filename, value, error))
+       {
+         value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
+         res = FALSE;
+         /* Don't set error multiple times */
+         error = NULL;
+       }
+      else
+       value->status = G_FILE_ATTRIBUTE_STATUS_SET;
+       
+    }
+#endif
+
+#ifdef HAVE_CHOWN
+  /* Group uid and gid setting into one call
+   * Change ownership before permissions, since ownership changes can
+     change permissions (e.g. setuid)
+   */
+  uid = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_UNIX_UID);
+  gid = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_UNIX_GID);
+  
+  if (uid || gid)
+    {
+      if (!set_unix_uid_gid (filename, uid, gid, flags, error))
+       {
+         status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
+         res = FALSE;
+         /* Don't set error multiple times */
+         error = NULL;
+       }
+      else
+       status = G_FILE_ATTRIBUTE_STATUS_SET;
+      if (uid)
+       uid->status = status;
+      if (gid)
+       gid->status = status;
+    }
+#endif
+  
+  value = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_UNIX_MODE);
+  if (value)
+    {
+      if (!set_unix_mode (filename, value, error))
+       {
+         value->status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
+         res = FALSE;
+         /* Don't set error multiple times */
+         error = NULL;
+       }
+      else
+       value->status = G_FILE_ATTRIBUTE_STATUS_SET;
+       
+    }
+
+#ifdef HAVE_UTIMES
+  /* Group all time settings into one call
+   * Change times as the last thing to avoid it changing due to metadata changes
+   */
+  
+  mtime = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
+  mtime_usec = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED_USEC);
+  atime = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS);
+  atime_usec = g_file_info_get_attribute (info, G_FILE_ATTRIBUTE_TIME_ACCESS_USEC);
+
+  if (mtime || mtime_usec || atime || atime_usec)
+    {
+      if (!set_mtime_atime (filename, mtime, mtime_usec, atime, atime_usec, error))
+       {
+         status = G_FILE_ATTRIBUTE_STATUS_ERROR_SETTING;
+         res = FALSE;
+         /* Don't set error multiple times */
+         error = NULL;
+       }
+      else
+       status = G_FILE_ATTRIBUTE_STATUS_SET;
+      
+      if (mtime)
+       mtime->status = status;
+      if (mtime_usec)
+       mtime_usec->status = status;
+      if (atime)
+       atime->status = status;
+      if (atime_usec)
+       atime_usec->status = status;
+    }
+#endif
+
+  /* xattrs are handled by default callback */
+
+  return res;
+}
+
+
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * ThumbMD5Context structure, pass it to thumb_md5_init, call
+ * thumb_md5_update as needed on buffers full of bytes, and then call
+ * thumb_md5_final, which will fill a supplied 32-byte array with the
+ * digest in ascii form. 
+ *
+ */
+
+static void thumb_md5_init      (struct ThumbMD5Context *context);
+static void thumb_md5_update    (struct ThumbMD5Context *context,
+                                unsigned char const    *buf,
+                                unsigned                len);
+static void thumb_md5_final     (unsigned char           digest[16],
+                                struct ThumbMD5Context *context);
+static void thumb_md5_transform (guint32                 buf[4],
+                                guint32 const           in[16]);
+
+
+static void
+thumb_md5 (const char *string, unsigned char digest[16])
+{
+  struct ThumbMD5Context md5_context;
+  
+  thumb_md5_init (&md5_context);
+  thumb_md5_update (&md5_context, (unsigned char *)string, strlen (string));
+  thumb_md5_final (digest, &md5_context);
+}
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define byteReverse(buf, len)  /* Nothing */
+#else
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void
+byteReverse(unsigned char *buf, unsigned longs)
+{
+    guint32 t;
+    do {
+       t = (guint32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(guint32 *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+static void 
+thumb_md5_init (struct ThumbMD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void 
+thumb_md5_update (struct ThumbMD5Context *ctx,
+                 unsigned char const *buf,
+                 unsigned len)
+{
+    guint32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((guint32) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           memcpy (p, buf, len);
+           return;
+       }
+       memcpy (p, buf, t);
+       byteReverse (ctx->in, 16);
+       thumb_md5_transform (ctx->buf, (guint32 *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       memcpy (ctx->in, buf, 64);
+       byteReverse (ctx->in, 16);
+       thumb_md5_transform (ctx->buf, (guint32 *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void 
+thumb_md5_final (unsigned char digest[16], struct ThumbMD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       memset (p, 0, count);
+       byteReverse (ctx->in, 16);
+       thumb_md5_transform (ctx->buf, (guint32 *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((guint32 *) ctx->in)[14] = ctx->bits[0];
+    ((guint32 *) ctx->in)[15] = ctx->bits[1];
+
+    thumb_md5_transform (ctx->buf, (guint32 *) ctx->in);
+    byteReverse ((unsigned char *) ctx->buf, 4);
+    memcpy (digest, ctx->buf, 16);
+    memset (ctx, 0, sizeof(ctx));      /* In case it's sensitive */
+}
+
+
+/* The four core functions - F1 is optimized somewhat */
+
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1 (z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define thumb_md5_step(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  ThumbMD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void 
+thumb_md5_transform (guint32 buf[4], guint32 const in[16])
+{
+    register guint32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    thumb_md5_step(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    thumb_md5_step(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    thumb_md5_step(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    thumb_md5_step(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    thumb_md5_step(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    thumb_md5_step(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    thumb_md5_step(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    thumb_md5_step(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    thumb_md5_step(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    thumb_md5_step(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    thumb_md5_step(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    thumb_md5_step(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    thumb_md5_step(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    thumb_md5_step(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    thumb_md5_step(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    thumb_md5_step(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+               
+    thumb_md5_step(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    thumb_md5_step(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    thumb_md5_step(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    thumb_md5_step(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    thumb_md5_step(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    thumb_md5_step(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    thumb_md5_step(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    thumb_md5_step(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    thumb_md5_step(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    thumb_md5_step(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    thumb_md5_step(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    thumb_md5_step(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    thumb_md5_step(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    thumb_md5_step(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    thumb_md5_step(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    thumb_md5_step(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+               
+    thumb_md5_step(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    thumb_md5_step(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    thumb_md5_step(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    thumb_md5_step(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    thumb_md5_step(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    thumb_md5_step(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    thumb_md5_step(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    thumb_md5_step(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    thumb_md5_step(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    thumb_md5_step(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    thumb_md5_step(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    thumb_md5_step(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    thumb_md5_step(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    thumb_md5_step(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    thumb_md5_step(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    thumb_md5_step(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+               
+    thumb_md5_step(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    thumb_md5_step(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    thumb_md5_step(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    thumb_md5_step(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    thumb_md5_step(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    thumb_md5_step(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    thumb_md5_step(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    thumb_md5_step(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    thumb_md5_step(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    thumb_md5_step(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    thumb_md5_step(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    thumb_md5_step(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    thumb_md5_step(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    thumb_md5_step(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    thumb_md5_step(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    thumb_md5_step(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
diff --git a/gio/glocalfileinfo.h b/gio/glocalfileinfo.h
new file mode 100644 (file)
index 0000000..888d5ba
--- /dev/null
@@ -0,0 +1,68 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_FILE_INFO_H__
+#define __G_LOCAL_FILE_INFO_H__
+
+#include <gio/gfileinfo.h>
+#include <gio/gfile.h>
+#include <sys/stat.h>
+
+G_BEGIN_DECLS
+
+typedef struct {
+  gboolean writable;
+  gboolean is_sticky;
+  int owner;
+  dev_t device;
+} GLocalParentFileInfo;
+
+void       _g_local_file_info_get_parent_info (const char                 *dir,
+                                              GFileAttributeMatcher      *attribute_matcher,
+                                              GLocalParentFileInfo       *parent_info);
+GFileInfo *_g_local_file_info_get             (const char                 *basename,
+                                              const char                 *path,
+                                              GFileAttributeMatcher      *attribute_matcher,
+                                              GFileQueryInfoFlags         flags,
+                                              GLocalParentFileInfo       *parent_info,
+                                              GError                    **error);
+GFileInfo *_g_local_file_info_get_from_fd     (int                         fd,
+                                              char                       *attributes,
+                                              GError                    **error);
+char *     _g_local_file_info_create_etag     (struct stat                *statbuf);
+gboolean   _g_local_file_info_set_attribute   (char                       *filename,
+                                              const char                 *attribute,
+                                              const GFileAttributeValue  *value,
+                                              GFileQueryInfoFlags         flags,
+                                              GCancellable               *cancellable,
+                                              GError                    **error);
+gboolean   _g_local_file_info_set_attributes  (char                       *filename,
+                                              GFileInfo                  *info,
+                                              GFileQueryInfoFlags         flags,
+                                              GCancellable               *cancellable,
+                                              GError                    **error);
+
+G_END_DECLS
+
+#endif /* __G_FILE_LOCAL_FILE_INFO_H__ */
+
+
diff --git a/gio/glocalfileinputstream.c b/gio/glocalfileinputstream.c
new file mode 100644 (file)
index 0000000..3f90720
--- /dev/null
@@ -0,0 +1,319 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "gioerror.h"
+#include "glocalfileinputstream.h"
+#include "glocalfileinfo.h"
+#include "glibintl.h"
+
+
+G_DEFINE_TYPE (GLocalFileInputStream, g_local_file_input_stream, G_TYPE_FILE_INPUT_STREAM);
+
+struct _GLocalFileInputStreamPrivate {
+  int fd;
+};
+
+static gssize     g_local_file_input_stream_read       (GInputStream      *stream,
+                                                       void              *buffer,
+                                                       gsize              count,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static gssize     g_local_file_input_stream_skip       (GInputStream      *stream,
+                                                       gsize              count,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static gboolean   g_local_file_input_stream_close      (GInputStream      *stream,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static goffset    g_local_file_input_stream_tell       (GFileInputStream  *stream);
+static gboolean   g_local_file_input_stream_can_seek   (GFileInputStream  *stream);
+static gboolean   g_local_file_input_stream_seek       (GFileInputStream  *stream,
+                                                       goffset            offset,
+                                                       GSeekType          type,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+static GFileInfo *g_local_file_input_stream_query_info (GFileInputStream  *stream,
+                                                       char              *attributes,
+                                                       GCancellable      *cancellable,
+                                                       GError           **error);
+
+static void
+g_local_file_input_stream_finalize (GObject *object)
+{
+  GLocalFileInputStream *file;
+  
+  file = G_LOCAL_FILE_INPUT_STREAM (object);
+  
+  if (G_OBJECT_CLASS (g_local_file_input_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_local_file_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_local_file_input_stream_class_init (GLocalFileInputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+  GFileInputStreamClass *file_stream_class = G_FILE_INPUT_STREAM_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GLocalFileInputStreamPrivate));
+  
+  gobject_class->finalize = g_local_file_input_stream_finalize;
+
+  stream_class->read = g_local_file_input_stream_read;
+  stream_class->skip = g_local_file_input_stream_skip;
+  stream_class->close = g_local_file_input_stream_close;
+  file_stream_class->tell = g_local_file_input_stream_tell;
+  file_stream_class->can_seek = g_local_file_input_stream_can_seek;
+  file_stream_class->seek = g_local_file_input_stream_seek;
+  file_stream_class->query_info = g_local_file_input_stream_query_info;
+}
+
+static void
+g_local_file_input_stream_init (GLocalFileInputStream *info)
+{
+  info->priv = G_TYPE_INSTANCE_GET_PRIVATE (info,
+                                           G_TYPE_LOCAL_FILE_INPUT_STREAM,
+                                           GLocalFileInputStreamPrivate);
+}
+
+/**
+ * g_local_file_input_stream_new:
+ * @fd: File Descriptor.
+ * 
+ * Returns: #GFileInputStream for the given file descriptor.
+ **/
+GFileInputStream *
+g_local_file_input_stream_new (int fd)
+{
+  GLocalFileInputStream *stream;
+
+  stream = g_object_new (G_TYPE_LOCAL_FILE_INPUT_STREAM, NULL);
+  stream->priv->fd = fd;
+  
+  return G_FILE_INPUT_STREAM (stream);
+}
+
+static gssize
+g_local_file_input_stream_read (GInputStream *stream,
+                               void         *buffer,
+                               gsize         count,
+                               GCancellable *cancellable,
+                               GError      **error)
+{
+  GLocalFileInputStream *file;
+  gssize res;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+
+  res = -1;
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       break;
+      res = read (file->priv->fd, buffer, count);
+      if (res == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error reading from file: %s"),
+                      g_strerror (errno));
+       }
+      
+      break;
+    }
+  
+  return res;
+}
+
+static gssize
+g_local_file_input_stream_skip (GInputStream *stream,
+                               gsize         count,
+                               GCancellable *cancellable,
+                               GError      **error)
+{
+  off_t res, start;
+  GLocalFileInputStream *file;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+  
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return -1;
+  
+  start = lseek (file->priv->fd, 0, SEEK_CUR);
+  if (start == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errno));
+      return -1;
+    }
+  
+  res = lseek (file->priv->fd, count, SEEK_CUR);
+  if (res == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errno));
+      return -1;
+    }
+
+  return res - start;
+}
+
+static gboolean
+g_local_file_input_stream_close (GInputStream *stream,
+                                GCancellable *cancellable,
+                                GError      **error)
+{
+  GLocalFileInputStream *file;
+  int res;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+
+  if (file->priv->fd == -1)
+    return TRUE;
+
+  while (1)
+    {
+      res = close (file->priv->fd);
+      if (res == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error closing file: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+
+  return res != -1;
+}
+
+
+static goffset
+g_local_file_input_stream_tell (GFileInputStream *stream)
+{
+  GLocalFileInputStream *file;
+  off_t pos;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1)
+    return 0;
+  
+  return pos;
+}
+
+static gboolean
+g_local_file_input_stream_can_seek (GFileInputStream *stream)
+{
+  GLocalFileInputStream *file;
+  off_t pos;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1 &&
+      errno == ESPIPE)
+    return FALSE;
+  
+  return TRUE;
+}
+
+static int
+seek_type_to_lseek (GSeekType type)
+{
+  switch (type)
+    {
+    default:
+    case G_SEEK_CUR:
+      return SEEK_CUR;
+      
+    case G_SEEK_SET:
+      return SEEK_SET;
+      
+    case G_SEEK_END:
+      return SEEK_END;
+    }
+}
+
+static gboolean
+g_local_file_input_stream_seek (GFileInputStream     *stream,
+                               goffset               offset,
+                               GSeekType             type,
+                               GCancellable         *cancellable,
+                               GError              **error)
+{
+  GLocalFileInputStream *file;
+  off_t pos;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+
+  pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
+
+  if (pos == (off_t)-1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static GFileInfo *
+g_local_file_input_stream_query_info (GFileInputStream     *stream,
+                                     char                 *attributes,
+                                     GCancellable         *cancellable,
+                                     GError              **error)
+{
+  GLocalFileInputStream *file;
+
+  file = G_LOCAL_FILE_INPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  return _g_local_file_info_get_from_fd (file->priv->fd,
+                                        attributes,
+                                        error);
+}
diff --git a/gio/glocalfileinputstream.h b/gio/glocalfileinputstream.h
new file mode 100644 (file)
index 0000000..259ed8e
--- /dev/null
@@ -0,0 +1,60 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_FILE_INPUT_STREAM_H__
+#define __G_LOCAL_FILE_INPUT_STREAM_H__
+
+#include <gio/gfileinputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_FILE_INPUT_STREAM         (g_local_file_input_stream_get_type ())
+#define G_LOCAL_FILE_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_FILE_INPUT_STREAM, GLocalFileInputStream))
+#define G_LOCAL_FILE_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LOCAL_FILE_INPUT_STREAM, GLocalFileInputStreamClass))
+#define G_IS_LOCAL_FILE_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_FILE_INPUT_STREAM))
+#define G_IS_LOCAL_FILE_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_FILE_INPUT_STREAM))
+#define G_LOCAL_FILE_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LOCAL_FILE_INPUT_STREAM, GLocalFileInputStreamClass))
+
+typedef struct _GLocalFileInputStream         GLocalFileInputStream;
+typedef struct _GLocalFileInputStreamClass    GLocalFileInputStreamClass;
+typedef struct _GLocalFileInputStreamPrivate  GLocalFileInputStreamPrivate;
+
+struct _GLocalFileInputStream
+{
+  GFileInputStream parent;
+
+  /*< private >*/
+  GLocalFileInputStreamPrivate *priv;
+};
+
+struct _GLocalFileInputStreamClass
+{
+  GFileInputStreamClass parent_class;
+};
+
+GType g_local_file_input_stream_get_type (void) G_GNUC_CONST;
+
+GFileInputStream *g_local_file_input_stream_new (int fd);
+
+G_END_DECLS
+
+#endif /* __G_LOCAL_FILE_INPUT_STREAM_H__ */
diff --git a/gio/glocalfilemonitor.c b/gio/glocalfilemonitor.c
new file mode 100644 (file)
index 0000000..94ebf7b
--- /dev/null
@@ -0,0 +1,211 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "glocalfilemonitor.h"
+#include "giomodule.h"
+
+#include <string.h>
+
+enum
+{
+  PROP_0,
+  PROP_FILENAME
+};
+
+G_DEFINE_ABSTRACT_TYPE (GLocalFileMonitor, g_local_file_monitor, G_TYPE_FILE_MONITOR)
+
+static void
+g_local_file_monitor_init (GLocalFileMonitor* local_monitor)
+{
+}
+
+static void
+g_local_file_monitor_set_property (GObject *object,
+                                   guint property_id,
+                                   const GValue *value,
+                                   GParamSpec *pspec)
+{
+  switch (property_id)
+  {
+    case PROP_FILENAME:
+      /* Do nothing */
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+      break;
+  }
+}
+
+static GObject *
+g_local_file_monitor_constructor (GType type,
+                                  guint n_construct_properties,
+                                  GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GLocalFileMonitorClass *klass;
+  GObjectClass *parent_class;
+  GLocalFileMonitor *local_monitor;
+  const gchar *filename = NULL;
+  gint i;
+  
+  klass = G_LOCAL_FILE_MONITOR_CLASS (g_type_class_peek (G_TYPE_LOCAL_FILE_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  local_monitor = G_LOCAL_FILE_MONITOR (obj);
+
+  for (i = 0; i < n_construct_properties; i++)
+    {
+      if (strcmp ("filename", g_param_spec_get_name (construct_properties[i].pspec)) == 0)
+        {
+          g_assert (G_VALUE_HOLDS_STRING (construct_properties[i].value));
+          filename = g_value_get_string (construct_properties[i].value);
+          break;
+        }
+    }
+
+  g_assert (filename != NULL);
+
+  local_monitor->filename = g_strdup (filename);
+  return obj;
+}
+
+static void
+g_local_file_monitor_finalize (GObject *object)
+{
+  GLocalFileMonitor *local_monitor = G_LOCAL_FILE_MONITOR (object);
+  if (local_monitor->filename)
+    {
+      g_free (local_monitor->filename);
+      local_monitor->filename = NULL;
+    }
+
+  if (G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_local_file_monitor_parent_class)->finalize) (object);
+}
+
+static void
+g_local_file_monitor_class_init (GLocalFileMonitorClass* klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->set_property = g_local_file_monitor_set_property;
+  gobject_class->finalize = g_local_file_monitor_finalize;
+  gobject_class->constructor = g_local_file_monitor_constructor;
+
+  g_object_class_install_property (gobject_class, PROP_FILENAME,
+      g_param_spec_string ("filename", "File name", "File name to monitor",
+          NULL, G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
+}
+
+static gint
+_compare_monitor_class_by_prio (gconstpointer a,
+                                gconstpointer b,
+                                gpointer user_data)
+{
+  GType *type1 = (GType *) a, *type2 = (GType *) b;
+  GLocalFileMonitorClass *klass1, *klass2;
+  gint ret;
+
+  klass1 = G_LOCAL_FILE_MONITOR_CLASS (g_type_class_ref (*type1));
+  klass2 = G_LOCAL_FILE_MONITOR_CLASS (g_type_class_ref (*type2));
+
+  ret = klass1->prio - klass2->prio;
+
+  g_type_class_unref (klass1);
+  g_type_class_unref (klass2);
+
+  return ret;
+}
+
+extern GType g_inotify_file_monitor_get_type (void);
+
+static gpointer
+get_default_local_file_monitor (gpointer data)
+{
+  GType *monitor_impls, chosen_type;
+  guint n_monitor_impls;
+  gint i;
+  GType *ret = (GType *) data;
+
+#if defined(HAVE_SYS_INOTIFY_H) || defined(HAVE_LINUX_INOTIFY_H)
+  /* Register Inotify monitor */
+  g_inotify_file_monitor_get_type ();
+#endif
+
+  g_io_modules_ensure_loaded (GIO_MODULE_DIR);
+  
+  monitor_impls = g_type_children (G_TYPE_LOCAL_FILE_MONITOR,
+                                   &n_monitor_impls);
+
+  chosen_type = G_TYPE_INVALID;
+
+  g_qsort_with_data (monitor_impls,
+                     n_monitor_impls,
+                     sizeof (GType),
+                     _compare_monitor_class_by_prio,
+                     NULL);
+
+  for (i = n_monitor_impls - 1; i >= 0 && chosen_type == G_TYPE_INVALID; i--)
+    {    
+      GLocalFileMonitorClass *klass;
+
+      klass = G_LOCAL_FILE_MONITOR_CLASS (g_type_class_ref (monitor_impls[i]));
+
+      if (klass->is_supported())
+        chosen_type = monitor_impls[i];
+
+      g_type_class_unref (klass);
+    }
+
+  g_free (monitor_impls);
+
+  *ret = chosen_type;
+
+  return NULL;
+}
+
+/**
+ * g_local_file_monitor_new:
+ * @pathname: path name to monitor.
+ * @flags: #GFileMonitorFlags.
+ * 
+ * Returns: a new #GFileMonitor for the given @pathname. 
+ **/
+GFileMonitor*
+g_local_file_monitor_new (const char* pathname,
+                          GFileMonitorFlags flags)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  static GType monitor_type = G_TYPE_INVALID;
+
+  g_once (&once_init, get_default_local_file_monitor, &monitor_type);
+
+  if (monitor_type != G_TYPE_INVALID)
+    return G_FILE_MONITOR (g_object_new (monitor_type, "filename", pathname, NULL));
+
+  return NULL;
+}
diff --git a/gio/glocalfilemonitor.h b/gio/glocalfilemonitor.h
new file mode 100644 (file)
index 0000000..b11ce85
--- /dev/null
@@ -0,0 +1,59 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_FILE_MONITOR_H__
+#define __G_LOCAL_FILE_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gfilemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_FILE_MONITOR              (g_local_file_monitor_get_type ())
+#define G_LOCAL_FILE_MONITOR(o)                        (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_FILE_MONITOR, GLocalFileMonitor))
+#define G_LOCAL_FILE_MONITOR_CLASS(k)          (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_LOCAL_FILE_MONITOR, GLocalFileMonitorClass))
+#define G_IS_LOCAL_FILE_MONITOR(o)             (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_FILE_MONITOR))
+#define G_IS_LOCAL_FILE_MONITOR_CLASS(k)       (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_FILE_MONITOR))
+
+typedef struct _GLocalFileMonitor      GLocalFileMonitor;
+typedef struct _GLocalFileMonitorClass GLocalFileMonitorClass;
+
+struct _GLocalFileMonitor
+{
+  GFileMonitor parent_instance;
+  gchar *filename;
+};
+
+struct _GLocalFileMonitorClass {
+  GFileMonitorClass parent_class;
+  gint prio;
+  gboolean (*is_supported) (void);
+};
+
+GType g_local_file_monitor_get_type (void) G_GNUC_CONST;
+
+GFileMonitor* g_local_file_monitor_new (const char* pathname,
+                                       GFileMonitorFlags flags);
+
+G_END_DECLS
+
+#endif /* __G_LOCAL_FILE_MONITOR_H__ */
diff --git a/gio/glocalfileoutputstream.c b/gio/glocalfileoutputstream.c
new file mode 100644 (file)
index 0000000..c8b58c9
--- /dev/null
@@ -0,0 +1,910 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "glibintl.h"
+#include "gioerror.h"
+#include "glocalfileoutputstream.h"
+#include "glocalfileinfo.h"
+
+G_DEFINE_TYPE (GLocalFileOutputStream, g_local_file_output_stream, G_TYPE_FILE_OUTPUT_STREAM);
+
+/* Some of the file replacement code was based on the code from gedit,
+ * relicenced to LGPL with permissions from the authors.
+ */
+
+#define BACKUP_EXTENSION "~"
+
+struct _GLocalFileOutputStreamPrivate {
+  char *tmp_filename;
+  char *original_filename;
+  char *backup_filename;
+  char *etag;
+  int fd;
+};
+
+static gssize     g_local_file_output_stream_write        (GOutputStream      *stream,
+                                                          const void         *buffer,
+                                                          gsize               count,
+                                                          GCancellable       *cancellable,
+                                                          GError            **error);
+static gboolean   g_local_file_output_stream_close        (GOutputStream      *stream,
+                                                          GCancellable       *cancellable,
+                                                          GError            **error);
+static GFileInfo *g_local_file_output_stream_query_info   (GFileOutputStream  *stream,
+                                                          char               *attributes,
+                                                          GCancellable       *cancellable,
+                                                          GError            **error);
+static char *     g_local_file_output_stream_get_etag     (GFileOutputStream  *stream);
+static goffset    g_local_file_output_stream_tell         (GFileOutputStream  *stream);
+static gboolean   g_local_file_output_stream_can_seek     (GFileOutputStream  *stream);
+static gboolean   g_local_file_output_stream_seek         (GFileOutputStream  *stream,
+                                                          goffset             offset,
+                                                          GSeekType           type,
+                                                          GCancellable       *cancellable,
+                                                          GError            **error);
+static gboolean   g_local_file_output_stream_can_truncate (GFileOutputStream  *stream);
+static gboolean   g_local_file_output_stream_truncate     (GFileOutputStream  *stream,
+                                                          goffset             size,
+                                                          GCancellable       *cancellable,
+                                                          GError            **error);
+
+static void
+g_local_file_output_stream_finalize (GObject *object)
+{
+  GLocalFileOutputStream *file;
+  
+  file = G_LOCAL_FILE_OUTPUT_STREAM (object);
+  
+  g_free (file->priv->tmp_filename);
+  g_free (file->priv->original_filename);
+  g_free (file->priv->backup_filename);
+  g_free (file->priv->etag);
+  
+  if (G_OBJECT_CLASS (g_local_file_output_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_local_file_output_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_local_file_output_stream_class_init (GLocalFileOutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+  GFileOutputStreamClass *file_stream_class = G_FILE_OUTPUT_STREAM_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GLocalFileOutputStreamPrivate));
+  
+  gobject_class->finalize = g_local_file_output_stream_finalize;
+
+  stream_class->write = g_local_file_output_stream_write;
+  stream_class->close = g_local_file_output_stream_close;
+  file_stream_class->query_info = g_local_file_output_stream_query_info;
+  file_stream_class->get_etag = g_local_file_output_stream_get_etag;
+  file_stream_class->tell = g_local_file_output_stream_tell;
+  file_stream_class->can_seek = g_local_file_output_stream_can_seek;
+  file_stream_class->seek = g_local_file_output_stream_seek;
+  file_stream_class->can_truncate = g_local_file_output_stream_can_truncate;
+  file_stream_class->truncate = g_local_file_output_stream_truncate;
+}
+
+static void
+g_local_file_output_stream_init (GLocalFileOutputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                             G_TYPE_LOCAL_FILE_OUTPUT_STREAM,
+                                             GLocalFileOutputStreamPrivate);
+}
+
+static gssize
+g_local_file_output_stream_write (GOutputStream *stream,
+                                 const void   *buffer,
+                                 gsize         count,
+                                 GCancellable *cancellable,
+                                 GError      **error)
+{
+  GLocalFileOutputStream *file;
+  gssize res;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       return -1;
+      res = write (file->priv->fd, buffer, count);
+      if (res == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error writing to file: %s"),
+                      g_strerror (errno));
+       }
+      
+      break;
+    }
+  
+  return res;
+}
+
+static gboolean
+g_local_file_output_stream_close (GOutputStream *stream,
+                                 GCancellable *cancellable,
+                                 GError      **error)
+{
+  GLocalFileOutputStream *file;
+  struct stat final_stat;
+  int res;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+
+  if (file->priv->tmp_filename)
+    {
+      /* We need to move the temp file to its final place,
+       * and possibly create the backup file
+       */
+
+      if (file->priv->backup_filename)
+       {
+         if (g_cancellable_set_error_if_cancelled (cancellable, error))
+           goto err_out;
+         
+#ifdef HAVE_LINK
+         /* create original -> backup link, the original is then renamed over */
+         if (unlink (file->priv->backup_filename) != 0 &&
+             errno != ENOENT)
+           {
+             g_set_error (error, G_IO_ERROR,
+                          G_IO_ERROR_CANT_CREATE_BACKUP,
+                          _("Error removing old backup link: %s"),
+                          g_strerror (errno));
+             goto err_out;
+           }
+
+         if (link (file->priv->original_filename, file->priv->backup_filename) != 0)
+           {
+             g_set_error (error, G_IO_ERROR,
+                          G_IO_ERROR_CANT_CREATE_BACKUP,
+                          _("Error creating backup link: %s"),
+                          g_strerror (errno));
+             goto err_out;
+           }
+#else
+           /* If link not supported, just rename... */
+         if (!rename (file->priv->original_filename, file->priv->backup_filename) != 0)
+           {
+             g_set_error (error, G_IO_ERROR,
+                          G_IO_ERROR_CANT_CREATE_BACKUP,
+                          _("Error creating backup copy: %s"),
+                          g_strerror (errno));
+             goto err_out;
+           }
+#endif
+       }
+      
+
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       goto err_out;
+
+      /* tmp -> original */
+      if (rename (file->priv->tmp_filename, file->priv->original_filename) != 0)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error renamining temporary file: %s"),
+                      g_strerror (errno));
+         goto err_out;
+       }
+    }
+  
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    goto err_out;
+      
+  if (fstat (file->priv->fd, &final_stat) == 0)
+    file->priv->etag = _g_local_file_info_create_etag (&final_stat);
+
+  while (1)
+    {
+      res = close (file->priv->fd);
+      if (res == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error closing file: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+  
+  return res != -1;
+
+ err_out:
+  /* A simple try to close the fd in case we fail before the actual close */
+  close (file->priv->fd);
+  return FALSE;
+}
+
+static char *
+g_local_file_output_stream_get_etag (GFileOutputStream      *stream)
+{
+  GLocalFileOutputStream *file;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+  
+  return g_strdup (file->priv->etag);
+}
+
+static goffset
+g_local_file_output_stream_tell (GFileOutputStream *stream)
+{
+  GLocalFileOutputStream *file;
+  off_t pos;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1)
+    return 0;
+  
+  return pos;
+}
+
+static gboolean
+g_local_file_output_stream_can_seek (GFileOutputStream *stream)
+{
+  GLocalFileOutputStream *file;
+  off_t pos;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+  
+  pos = lseek (file->priv->fd, 0, SEEK_CUR);
+
+  if (pos == (off_t)-1 &&
+      errno == ESPIPE)
+    return FALSE;
+  
+  return TRUE;
+}
+
+static int
+seek_type_to_lseek (GSeekType type)
+{
+  switch (type)
+    {
+    default:
+    case G_SEEK_CUR:
+      return SEEK_CUR;
+      
+    case G_SEEK_SET:
+      return SEEK_SET;
+      
+    case G_SEEK_END:
+      return SEEK_END;
+    }
+}
+
+static gboolean
+g_local_file_output_stream_seek (GFileOutputStream     *stream,
+                                goffset               offset,
+                                GSeekType             type,
+                                GCancellable         *cancellable,
+                                GError              **error)
+{
+  GLocalFileOutputStream *file;
+  off_t pos;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+
+  pos = lseek (file->priv->fd, offset, seek_type_to_lseek (type));
+
+  if (pos == (off_t)-1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error seeking in file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+static gboolean
+g_local_file_output_stream_can_truncate (GFileOutputStream    *stream)
+{
+  /* We can't truncate pipes and stuff where we can't seek */
+  return g_local_file_output_stream_can_seek (stream);
+}
+
+static gboolean
+g_local_file_output_stream_truncate (GFileOutputStream    *stream,
+                                    goffset               size,
+                                    GCancellable         *cancellable,
+                                    GError              **error)
+{
+  GLocalFileOutputStream *file;
+  int res;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+
+ restart:
+  res = ftruncate(file->priv->fd, size);
+  
+  if (res == -1)
+    {
+      if (errno == EINTR)
+       {
+         if (g_cancellable_set_error_if_cancelled (cancellable, error))
+           return FALSE;
+         goto restart;
+       }
+
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error truncating file: %s"),
+                  g_strerror (errno));
+      return FALSE;
+    }
+  
+  return TRUE;
+}
+
+
+static GFileInfo *
+g_local_file_output_stream_query_info (GFileOutputStream     *stream,
+                                      char                 *attributes,
+                                      GCancellable         *cancellable,
+                                      GError              **error)
+{
+  GLocalFileOutputStream *file;
+
+  file = G_LOCAL_FILE_OUTPUT_STREAM (stream);
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+  
+  return _g_local_file_info_get_from_fd (file->priv->fd,
+                                        attributes,
+                                        error);
+}
+
+/**
+ * g_local_file_output_stream_create:
+ * @filename:
+ * @flags:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: #GFileOutputStream.
+ **/
+GFileOutputStream *
+g_local_file_output_stream_create  (const char       *filename,
+                                   GFileCreateFlags  flags,
+                                   GCancellable     *cancellable,
+                                   GError          **error)
+{
+  GLocalFileOutputStream *stream;
+  int mode;
+  int fd;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  if (flags & G_FILE_CREATE_FLAGS_PRIVATE)
+    mode = 0600;
+  else
+    mode = 0666;
+  
+  fd = g_open (filename,
+              O_CREAT | O_EXCL | O_WRONLY,
+              0666);
+  if (fd == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINVAL)
+       /* This must be an invalid filename, on e.g. FAT */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error opening file '%s': %s"),
+                    filename, g_strerror (errsv));
+      return NULL;
+    }
+  
+  stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
+  stream->priv->fd = fd;
+  return G_FILE_OUTPUT_STREAM (stream);
+}
+
+/**
+ * g_local_file_output_stream_append:
+ * @filename:
+ * @flags:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+GFileOutputStream *
+g_local_file_output_stream_append  (const char       *filename,
+                                   GFileCreateFlags  flags,
+                                   GCancellable     *cancellable,
+                                   GError          **error)
+{
+  GLocalFileOutputStream *stream;
+  int mode;
+  int fd;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  if (flags & G_FILE_CREATE_FLAGS_PRIVATE)
+    mode = 0600;
+  else
+    mode = 0666;
+
+  fd = g_open (filename,
+              O_CREAT | O_APPEND | O_WRONLY,
+              mode);
+  if (fd == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINVAL)
+       /* This must be an invalid filename, on e.g. FAT */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error opening file '%s': %s"),
+                    filename, g_strerror (errsv));
+      return NULL;
+    }
+  
+  stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
+  stream->priv->fd = fd;
+  
+  return G_FILE_OUTPUT_STREAM (stream);
+}
+
+static char *
+create_backup_filename (const char *filename)
+{
+  return g_strconcat (filename, BACKUP_EXTENSION, NULL);
+}
+
+#define BUFSIZE        8192 /* size of normal write buffer */
+
+static gboolean
+copy_file_data (gint     sfd,
+               gint     dfd,
+               GError **error)
+{
+  gboolean ret = TRUE;
+  gpointer buffer;
+  const gchar *write_buffer;
+  ssize_t bytes_read;
+  ssize_t bytes_to_write;
+  ssize_t bytes_written;
+  
+  buffer = g_malloc (BUFSIZE);
+  
+  do
+    {
+      bytes_read = read (sfd, buffer, BUFSIZE);
+      if (bytes_read == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error reading from file: %s"),
+                      g_strerror (errno));
+         ret = FALSE;
+         break;
+       }
+      
+      bytes_to_write = bytes_read;
+      write_buffer = buffer;
+      
+      do
+       {
+         bytes_written = write (dfd, write_buffer, bytes_to_write);
+         if (bytes_written == -1)
+           {
+             if (errno == EINTR)
+               continue;
+             
+             g_set_error (error, G_IO_ERROR,
+                          g_io_error_from_errno (errno),
+                          _("Error writing to file: %s"),
+                          g_strerror (errno));
+             ret = FALSE;
+             break;
+           }
+         
+         bytes_to_write -= bytes_written;
+         write_buffer += bytes_written;
+       }
+      while (bytes_to_write > 0);
+      
+    } while ((bytes_read != 0) && (ret == TRUE));
+
+  g_free (buffer);
+  
+  return ret;
+}
+
+static int
+handle_overwrite_open (const char *filename,
+                      const char *etag,
+                      gboolean create_backup,
+                      char **temp_filename,
+                      GCancellable *cancellable,
+                      GError      **error)
+{
+  int fd = -1;
+  struct stat original_stat;
+  char *current_etag;
+  gboolean is_symlink;
+  int open_flags;
+
+  /* We only need read access to the original file if we are creating a backup.
+   * We also add O_CREATE to avoid a race if the file was just removed */
+  if (create_backup)
+    open_flags = O_RDWR | O_CREAT;
+  else
+    open_flags = O_WRONLY | O_CREAT;
+  
+  /* Some systems have O_NOFOLLOW, which lets us avoid some races
+   * when finding out if the file we opened was a symlink */
+#ifdef O_NOFOLLOW
+  is_symlink = FALSE;
+  fd = g_open (filename, open_flags | O_NOFOLLOW, 0666);
+  if (fd == -1 && errno == ELOOP)
+    {
+      /* Could be a symlink, or it could be a regular ELOOP error,
+       * but then the next open will fail too. */
+      is_symlink = TRUE;
+      fd = g_open (filename, open_flags, 0666);
+    }
+#else
+  fd = g_open (filename, open_flags, 0666);
+  /* This is racy, but we do it as soon as possible to minimize the race */
+  is_symlink = g_file_test (filename, G_FILE_TEST_IS_SYMLINK);
+#endif
+    
+  if (fd == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error opening file '%s': %s"),
+                  filename, g_strerror (errno));
+      return -1;
+    }
+  
+  if (fstat (fd, &original_stat) != 0) 
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error stating file '%s': %s"),
+                  filename, g_strerror (errno));
+      goto err_out;
+    }
+  
+  /* not a regular file */
+  if (!S_ISREG (original_stat.st_mode))
+    {
+      if (S_ISDIR (original_stat.st_mode))
+       g_set_error (error,
+                    G_IO_ERROR,
+                    G_IO_ERROR_IS_DIRECTORY,
+                    _("Target file is a directory"));
+      else
+       g_set_error (error,
+                    G_IO_ERROR,
+                    G_IO_ERROR_NOT_REGULAR_FILE,
+                    _("Target file is not a regular file"));
+      goto err_out;
+    }
+  
+  if (etag != NULL)
+    {
+      current_etag = _g_local_file_info_create_etag (&original_stat);
+      if (strcmp (etag, current_etag) != 0)
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_WRONG_ETAG,
+                      _("The file was externally modified"));
+         g_free (current_etag);
+         goto err_out;
+       }
+      g_free (current_etag);
+    }
+
+  /* We use two backup strategies.
+   * The first one (which is faster) consist in saving to a
+   * tmp file then rename the original file to the backup and the
+   * tmp file to the original name. This is fast but doesn't work
+   * when the file is a link (hard or symbolic) or when we can't
+   * write to the current dir or can't set the permissions on the
+   * new file. 
+   * The second strategy consist simply in copying the old file
+   * to a backup file and rewrite the contents of the file.
+   */
+  
+  if (!(original_stat.st_nlink > 1) && !is_symlink)
+    {
+      char *dirname, *tmp_filename;
+      int tmpfd;
+      
+      dirname = g_path_get_dirname (filename);
+      tmp_filename = g_build_filename (dirname, ".goutputstream-XXXXXX", NULL);
+      g_free (dirname);
+
+      tmpfd = g_mkstemp (tmp_filename);
+      if (tmpfd == -1)
+       {
+         g_free (tmp_filename);
+         goto fallback_strategy;
+       }
+      
+      /* try to keep permissions */
+
+      if (
+#ifdef F_CHOWN
+         fchown (tmpfd, original_stat.st_uid, original_stat.st_gid) == -1 ||
+#endif
+#ifdef F_CHMOD
+         fchmod (tmpfd, original_stat.st_mode) == -1 ||
+#endif
+         0
+         )
+       {
+         struct stat tmp_statbuf;
+         
+         /* Check that we really needed to change something */
+         if (fstat (tmpfd, &tmp_statbuf) != 0 ||
+             original_stat.st_uid != tmp_statbuf.st_uid ||
+             original_stat.st_gid != tmp_statbuf.st_gid ||
+             original_stat.st_mode != tmp_statbuf.st_mode)
+           {
+             close (tmpfd);
+             unlink (tmp_filename);
+             g_free (tmp_filename);
+             goto fallback_strategy;
+           }
+       }
+
+      close (fd);
+      *temp_filename = tmp_filename;
+      return tmpfd;
+    }
+
+ fallback_strategy:
+
+  if (create_backup)
+    {
+      struct stat tmp_statbuf;      
+      char *backup_filename;
+      int bfd;
+      
+      backup_filename = create_backup_filename (filename);
+
+      if (unlink (backup_filename) == -1 && errno != ENOENT)
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_CANT_CREATE_BACKUP,
+                      _("Backup file creation failed"));
+         g_free (backup_filename);
+         goto err_out;
+       }
+
+      bfd = g_open (backup_filename,
+                   O_WRONLY | O_CREAT | O_EXCL,
+                   original_stat.st_mode & 0777);
+
+      if (bfd == -1)
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_CANT_CREATE_BACKUP,
+                      _("Backup file creation failed"));
+         g_free (backup_filename);
+         goto err_out;
+       }
+
+      /* If needed, Try to set the group of the backup same as the
+       * original file. If this fails, set the protection
+       * bits for the group same as the protection bits for
+       * others. */
+#ifdef HAVE_FCHOWN
+      if (fstat (bfd, &tmp_statbuf) != 0)
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_CANT_CREATE_BACKUP,
+                      _("Backup file creation failed"));
+         unlink (backup_filename);
+         g_free (backup_filename);
+         goto err_out;
+       }
+      
+      if ((original_stat.st_gid != tmp_statbuf.st_gid)  &&
+         fchown (bfd, (uid_t) -1, original_stat.st_gid) != 0)
+       {
+         if (fchmod (bfd,
+                     (original_stat.st_mode & 0707) |
+                     ((original_stat.st_mode & 07) << 3)) != 0)
+           {
+             g_set_error (error,
+                          G_IO_ERROR,
+                          G_IO_ERROR_CANT_CREATE_BACKUP,
+                          _("Backup file creation failed"));
+             unlink (backup_filename);
+             close (bfd);
+             g_free (backup_filename);
+             goto err_out;
+           }
+       }
+#endif
+
+      if (!copy_file_data (fd, bfd, NULL))
+       {
+         g_set_error (error,
+                      G_IO_ERROR,
+                      G_IO_ERROR_CANT_CREATE_BACKUP,
+                      _("Backup file creation failed"));
+         unlink (backup_filename);
+         close (bfd);
+         g_free (backup_filename);
+         
+         goto err_out;
+       }
+      
+      close (bfd);
+      g_free (backup_filename);
+
+      /* Seek back to the start of the file after the backup copy */
+      if (lseek (fd, 0, SEEK_SET) == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error seeking in file: %s"),
+                      g_strerror (errno));
+         goto err_out;
+       }
+    }
+
+  /* Truncate the file at the start */
+  if (ftruncate (fd, 0) == -1)
+    {
+      g_set_error (error, G_IO_ERROR,
+                  g_io_error_from_errno (errno),
+                  _("Error truncating file: %s"),
+                  g_strerror (errno));
+      goto err_out;
+    }
+    
+  return fd;
+
+ err_out:
+  close (fd);
+  return -1;
+}
+
+/**
+ * g_local_file_output_stream_replace:
+ * @filename: the file name.
+ * @etag:
+ * @create_backup: if set, create a backup of the file.
+ * @flags:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: #GFileOutputStream
+ **/
+GFileOutputStream *
+g_local_file_output_stream_replace (const char        *filename,
+                                   const char        *etag,
+                                   gboolean           create_backup,
+                                   GFileCreateFlags   flags,
+                                   GCancellable      *cancellable,
+                                   GError           **error)
+{
+  GLocalFileOutputStream *stream;
+  int mode;
+  int fd;
+  char *temp_file;
+
+  if (g_cancellable_set_error_if_cancelled (cancellable, error))
+    return NULL;
+
+  temp_file = NULL;
+
+  if (flags & G_FILE_CREATE_FLAGS_PRIVATE)
+    mode = 0600;
+  else
+    mode = 0666;
+
+  /* If the file doesn't exist, create it */
+  fd = g_open (filename,
+              O_CREAT | O_EXCL | O_WRONLY,
+              mode);
+
+  if (fd == -1 && errno == EEXIST)
+    {
+      /* The file already exists */
+      fd = handle_overwrite_open (filename, etag, create_backup, &temp_file,
+                                 cancellable, error);
+      if (fd == -1)
+       return NULL;
+    }
+  else if (fd == -1)
+    {
+      int errsv = errno;
+
+      if (errsv == EINVAL)
+       /* This must be an invalid filename, on e.g. FAT */
+       g_set_error (error, G_IO_ERROR,
+                    G_IO_ERROR_INVALID_FILENAME,
+                    _("Invalid filename"));
+      else
+       g_set_error (error, G_IO_ERROR,
+                    g_io_error_from_errno (errsv),
+                    _("Error opening file '%s': %s"),
+                    filename, g_strerror (errsv));
+      return NULL;
+    }
+  
+  stream = g_object_new (G_TYPE_LOCAL_FILE_OUTPUT_STREAM, NULL);
+  stream->priv->fd = fd;
+  stream->priv->tmp_filename = temp_file;
+  if (create_backup)
+    stream->priv->backup_filename = create_backup_filename (filename);
+  stream->priv->original_filename =  g_strdup (filename);
+  
+  return G_FILE_OUTPUT_STREAM (stream);
+}
diff --git a/gio/glocalfileoutputstream.h b/gio/glocalfileoutputstream.h
new file mode 100644 (file)
index 0000000..fc8237d
--- /dev/null
@@ -0,0 +1,73 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_FILE_OUTPUT_STREAM_H__
+#define __G_LOCAL_FILE_OUTPUT_STREAM_H__
+
+#include <gio/gfileoutputstream.h>
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_FILE_OUTPUT_STREAM         (g_local_file_output_stream_get_type ())
+#define G_LOCAL_FILE_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_LOCAL_FILE_OUTPUT_STREAM, GLocalFileOutputStream))
+#define G_LOCAL_FILE_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_LOCAL_FILE_OUTPUT_STREAM, GLocalFileOutputStreamClass))
+#define G_IS_LOCAL_FILE_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_LOCAL_FILE_OUTPUT_STREAM))
+#define G_IS_LOCAL_FILE_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_LOCAL_FILE_OUTPUT_STREAM))
+#define G_LOCAL_FILE_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_LOCAL_FILE_OUTPUT_STREAM, GLocalFileOutputStreamClass))
+
+typedef struct _GLocalFileOutputStream         GLocalFileOutputStream;
+typedef struct _GLocalFileOutputStreamClass    GLocalFileOutputStreamClass;
+typedef struct _GLocalFileOutputStreamPrivate  GLocalFileOutputStreamPrivate;
+
+struct _GLocalFileOutputStream
+{
+  GFileOutputStream parent;
+
+  /*< private >*/
+  GLocalFileOutputStreamPrivate *priv;
+};
+
+struct _GLocalFileOutputStreamClass
+{
+  GFileOutputStreamClass parent_class;
+};
+
+GType g_local_file_output_stream_get_type (void) G_GNUC_CONST;
+GFileOutputStream *g_local_file_output_stream_create  (const char       *filename,
+                                                      GFileCreateFlags  flags,
+                                                      GCancellable     *cancellable,
+                                                      GError          **error);
+GFileOutputStream *g_local_file_output_stream_append  (const char       *filename,
+                                                      GFileCreateFlags  flags,
+                                                      GCancellable     *cancellable,
+                                                      GError          **error);
+GFileOutputStream *g_local_file_output_stream_replace (const char       *filename,
+                                                      const char       *etag,
+                                                      gboolean          create_backup,
+                                                      GFileCreateFlags  flags,
+                                                      GCancellable     *cancellable,
+                                                      GError          **error);
+
+G_END_DECLS
+
+#endif /* __G_LOCAL_FILE_OUTPUT_STREAM_H__ */
diff --git a/gio/glocalvfs.c b/gio/glocalvfs.c
new file mode 100644 (file)
index 0000000..8a39f98
--- /dev/null
@@ -0,0 +1,191 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "glocalvfs.h"
+#include "glocalfile.h"
+#include <gio/gdummyfile.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+struct _GLocalVfs
+{
+  GVfs parent;
+};
+
+struct _GLocalVfsClass
+{
+  GVfsClass parent_class;
+  
+};
+
+G_DEFINE_TYPE (GLocalVfs, g_local_vfs, G_TYPE_VFS)
+static void
+g_local_vfs_finalize (GObject *object)
+{
+  /* must chain up */
+  G_OBJECT_CLASS (g_local_vfs_parent_class)->finalize (object);
+}
+
+static void
+g_local_vfs_init (GLocalVfs *vfs)
+{
+}
+
+/**
+ * g_local_vfs_new:
+ * 
+ * Returns a new #GVfs handle.
+ **/
+GVfs *
+g_local_vfs_new (void)
+{
+  return g_object_new (G_TYPE_LOCAL_VFS, NULL);
+}
+
+static GFile *
+g_local_vfs_get_file_for_path  (GVfs       *vfs,
+                               const char *path)
+{
+  return g_local_file_new (path);
+}
+
+static GFile *
+g_local_vfs_get_file_for_uri   (GVfs       *vfs,
+                               const char *uri)
+{
+  char *path;
+  GFile *file;
+
+  path = g_filename_from_uri (uri, NULL, NULL);
+
+  if (path != NULL)
+    file = g_local_file_new (path);
+  else
+    file = g_dummy_file_new (uri);
+
+  g_free (path);
+
+  return file;
+}
+
+static const gchar * const *
+g_local_vfs_get_supported_uri_schemes (GVfs *vfs)
+{
+  static const gchar * uri_schemes[] = { "file", NULL };
+
+  return uri_schemes;
+}
+
+static GFile *
+g_local_vfs_parse_name (GVfs       *vfs,
+                       const char *parse_name)
+{
+  GFile *file;
+  char *filename;
+  char *user_name;
+  char *user_prefix;
+  const char *user_start, *user_end;
+  char *rest;
+  struct passwd *passwd_file_entry;
+  
+  g_return_val_if_fail (G_IS_VFS (vfs), NULL);
+  g_return_val_if_fail (parse_name != NULL, NULL);
+
+  if (g_ascii_strncasecmp ("file:", parse_name, 5) == 0)
+    filename = g_filename_from_uri (parse_name, NULL, NULL);
+  else
+    {
+      if (*parse_name == '~')
+       {
+         parse_name ++;
+         user_start = parse_name;
+         
+         while (*parse_name != 0 && *parse_name != '/')
+           parse_name++;
+         
+         user_end = parse_name;
+
+         if (user_end == user_start)
+           user_prefix = g_strdup (g_get_home_dir());
+         else
+           {
+             user_name = g_strndup (user_start, user_end - user_start);
+             passwd_file_entry = getpwnam (user_name);
+             g_free (user_name);
+             
+             if (passwd_file_entry != NULL &&
+                 passwd_file_entry->pw_dir != NULL)
+               user_prefix = g_strdup (passwd_file_entry->pw_dir);
+             else
+               user_prefix = g_strdup (g_get_home_dir ());
+           }
+
+         rest = NULL;
+         if (*user_end != 0)
+           rest = g_filename_from_utf8 (user_end, -1, NULL, NULL, NULL);
+         
+         filename = g_build_filename (user_prefix, rest, NULL);
+         g_free (rest);
+         g_free (user_prefix);
+       }
+      else
+       filename = g_filename_from_utf8 (parse_name, -1, NULL, NULL, NULL);
+    }
+  
+  if (filename == NULL)
+    filename = g_strdup (parse_name);
+    
+  file = g_local_file_new (filename);
+  g_free (filename);
+
+  return file;
+}
+
+static gboolean
+g_local_vfs_is_active (GVfs *vfs)
+{
+  return TRUE;
+}
+
+static void
+g_local_vfs_class_init (GLocalVfsClass *class)
+{
+  GObjectClass *object_class;
+  GVfsClass *vfs_class;
+  
+  object_class = (GObjectClass *) class;
+
+  object_class->finalize = g_local_vfs_finalize;
+
+  vfs_class = G_VFS_CLASS (class);
+
+  vfs_class->name = "local";
+  vfs_class->priority = 0;
+  
+  vfs_class->is_active = g_local_vfs_is_active;
+  vfs_class->get_file_for_path = g_local_vfs_get_file_for_path;
+  vfs_class->get_file_for_uri = g_local_vfs_get_file_for_uri;
+  vfs_class->get_supported_uri_schemes = g_local_vfs_get_supported_uri_schemes;
+  vfs_class->parse_name = g_local_vfs_parse_name;
+}
diff --git a/gio/glocalvfs.h b/gio/glocalvfs.h
new file mode 100644 (file)
index 0000000..dee124c
--- /dev/null
@@ -0,0 +1,47 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_LOCAL_VFS_H__
+#define __G_LOCAL_VFS_H__
+
+#include <gio/gvfs.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_LOCAL_VFS                       (g_local_vfs_get_type ())
+#define G_LOCAL_VFS(obj)                       (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_LOCAL_VFS, GLocalVfs))
+#define G_LOCAL_VFS_CLASS(klass)               (G_TYPE_CHECK_CLASS_CAST ((klass), G_TYPE_LOCAL_VFS, GLocalVfsClass))
+#define G_IS_LOCAL_VFS(obj)                    (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_LOCAL_VFS))
+#define G_IS_LOCAL_VFS_CLASS(klass)            (G_TYPE_CHECK_CLASS_TYPE ((klass), G_TYPE_LOCAL_VFS))
+#define G_LOCAL_VFS_GET_CLASS(obj)             (G_TYPE_INSTANCE_GET_CLASS ((obj), G_TYPE_LOCAL_VFS, GLocalVfsClass))
+
+typedef struct _GLocalVfs       GLocalVfs;
+typedef struct _GLocalVfsClass  GLocalVfsClass;
+
+GType   g_local_vfs_get_type  (void) G_GNUC_CONST;
+
+GVfs *g_local_vfs_new (void);
+
+
+G_END_DECLS
+
+#endif /* __G_LOCAL_VFS_H__ */
diff --git a/gio/gmemoryinputstream.c b/gio/gmemoryinputstream.c
new file mode 100644 (file)
index 0000000..fea27a4
--- /dev/null
@@ -0,0 +1,461 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#include <config.h>
+
+#include "gmemoryinputstream.h"
+#include "ginputstream.h"
+#include "gseekable.h"
+#include "string.h"
+#include "gsimpleasyncresult.h"
+
+#include "glibintl.h"
+
+struct _GMemoryInputStreamPrivate {
+  guint8 *buffer;      
+  gsize   pos;
+  gsize   len;
+  gboolean free_data;
+};
+
+static gssize   g_memory_input_stream_read         (GInputStream         *stream,
+                                                   void                 *buffer,
+                                                   gsize                 count,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static gssize   g_memory_input_stream_skip         (GInputStream         *stream,
+                                                   gsize                 count,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static gboolean g_memory_input_stream_close        (GInputStream         *stream,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static void     g_memory_input_stream_read_async   (GInputStream         *stream,
+                                                   void                 *buffer,
+                                                   gsize                 count,
+                                                   int                   io_priority,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              user_data);
+static gssize   g_memory_input_stream_read_finish  (GInputStream         *stream,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
+static void     g_memory_input_stream_skip_async   (GInputStream         *stream,
+                                                   gsize                 count,
+                                                   int                   io_priority,
+                                                   GCancellable         *cancellabl,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              datae);
+static gssize   g_memory_input_stream_skip_finish  (GInputStream         *stream,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
+static void     g_memory_input_stream_close_async  (GInputStream         *stream,
+                                                   int                   io_priority,
+                                                   GCancellable         *cancellabl,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              data);
+static gboolean g_memory_input_stream_close_finish (GInputStream         *stream,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
+
+static void     g_memory_input_stream_seekable_iface_init (GSeekableIface  *iface);
+static goffset  g_memory_input_stream_tell                (GSeekable       *seekable);
+static gboolean g_memory_input_stream_can_seek            (GSeekable       *seekable);
+static gboolean g_memory_input_stream_seek                (GSeekable       *seekable,
+                                                           goffset          offset,
+                                                           GSeekType        type,
+                                                           GCancellable    *cancellable,
+                                                           GError         **error);
+static gboolean g_memory_input_stream_can_truncate        (GSeekable       *seekable);
+static gboolean g_memory_input_stream_truncate            (GSeekable       *seekable,
+                                                           goffset          offset,
+                                                           GCancellable    *cancellable,
+                                                           GError         **error);
+static void     g_memory_input_stream_finalize            (GObject         *object);
+
+G_DEFINE_TYPE_WITH_CODE (GMemoryInputStream, g_memory_input_stream, G_TYPE_INPUT_STREAM,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+                                                g_memory_input_stream_seekable_iface_init))
+
+
+static void
+g_memory_input_stream_class_init (GMemoryInputStreamClass *klass)
+{
+  GObjectClass *object_class;
+  GInputStreamClass *istream_class;
+
+  g_type_class_add_private (klass, sizeof (GMemoryInputStreamPrivate));
+
+  object_class = G_OBJECT_CLASS (klass);
+  object_class->finalize     = g_memory_input_stream_finalize;
+  
+  istream_class = G_INPUT_STREAM_CLASS (klass);
+  istream_class->read  = g_memory_input_stream_read;
+  istream_class->skip  = g_memory_input_stream_skip;
+  istream_class->close = g_memory_input_stream_close;
+
+  istream_class->read_async  = g_memory_input_stream_read_async;
+  istream_class->read_finish  = g_memory_input_stream_read_finish;
+  istream_class->skip_async  = g_memory_input_stream_skip_async;
+  istream_class->skip_finish  = g_memory_input_stream_skip_finish;
+  istream_class->close_async = g_memory_input_stream_close_async;
+  istream_class->close_finish = g_memory_input_stream_close_finish;
+}
+
+static void
+g_memory_input_stream_finalize (GObject *object)
+{
+  GMemoryInputStream        *stream;
+
+  stream = G_MEMORY_INPUT_STREAM (object);
+
+  if (stream->priv->free_data)
+    g_free (stream->priv->buffer);
+
+  if (G_OBJECT_CLASS (g_memory_input_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_memory_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_memory_input_stream_seekable_iface_init (GSeekableIface *iface)
+{
+  iface->tell         = g_memory_input_stream_tell;
+  iface->can_seek     = g_memory_input_stream_can_seek;
+  iface->seek         = g_memory_input_stream_seek;
+  iface->can_truncate = g_memory_input_stream_can_truncate;
+  iface->truncate     = g_memory_input_stream_truncate;
+}
+
+static void
+g_memory_input_stream_init (GMemoryInputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                              G_TYPE_MEMORY_INPUT_STREAM,
+                                              GMemoryInputStreamPrivate);
+}
+
+/**
+ * g_memory_input_stream_set_free_data:
+ * @stream:
+ * @free_data:
+ * 
+ **/
+void
+g_memory_input_stream_set_free_data (GMemoryInputStream *stream,
+                                    gboolean free_data)
+{
+  g_return_if_fail (G_IS_MEMORY_INPUT_STREAM (stream));
+
+  stream->priv->free_data = free_data;
+}
+
+/**
+ * g_memory_input_stream_from_data:
+ * @data: input data.
+ * @len: length of the data.
+ * 
+ * Returns: new #GInputStream read from @data of @len bytes.
+ **/
+GInputStream *
+g_memory_input_stream_from_data (const void *data, gssize len)
+{
+  GInputStream *stream;
+  GMemoryInputStream *memory_stream;
+
+  g_return_val_if_fail (data != NULL, NULL);
+
+  stream = g_object_new (G_TYPE_MEMORY_INPUT_STREAM, NULL);
+  memory_stream = G_MEMORY_INPUT_STREAM (stream);
+
+  if (len == -1)
+    len = strlen (data);
+  
+  memory_stream->priv->buffer = (guint8 *)data;
+  memory_stream->priv->len = len;
+
+  return stream;
+}
+
+static gssize
+g_memory_input_stream_read (GInputStream *stream,
+                            void         *buffer,
+                            gsize         count,
+                            GCancellable *cancellable,
+                            GError      **error)
+{
+  GMemoryInputStream *memory_stream;
+  GMemoryInputStreamPrivate * priv;
+
+  memory_stream = G_MEMORY_INPUT_STREAM (stream);
+  priv = memory_stream->priv;
+
+  count = MIN (count, priv->len - priv->pos);
+  memcpy (buffer, priv->buffer + priv->pos, count);
+  priv->pos += count;
+
+  return count;
+}
+
+/**
+ * g_memory_input_stream_get_data:
+ * @stream:
+ * 
+ * Returns: 
+ **/
+const void *
+g_memory_input_stream_get_data (GMemoryInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_MEMORY_INPUT_STREAM (stream), NULL);
+
+  return stream->priv->buffer;
+}
+
+/**
+ * g_memory_input_stream_get_data_size:
+ * @stream:
+ * 
+ * Returns: 
+ **/
+gsize
+g_memory_input_stream_get_data_size (GMemoryInputStream *stream)
+{
+  g_return_val_if_fail (G_IS_MEMORY_INPUT_STREAM (stream), -1);
+
+  return stream->priv->len;
+}
+
+static gssize
+g_memory_input_stream_skip (GInputStream              *stream,
+                            gsize                      count,
+                            GCancellable              *cancellable,
+                            GError                   **error)
+{
+  GMemoryInputStream *memory_stream;
+  GMemoryInputStreamPrivate *priv;
+
+  memory_stream = G_MEMORY_INPUT_STREAM (stream);
+  priv = memory_stream->priv;
+
+  count = MIN (count, priv->len - priv->pos);
+  priv->pos += count;
+
+  return count;
+
+}
+
+static gboolean
+g_memory_input_stream_close (GInputStream *stream,
+                             GCancellable *cancellable,
+                             GError      **error)
+{
+  return TRUE;
+}
+
+static void
+g_memory_input_stream_read_async (GInputStream              *stream,
+                                  void                      *buffer,
+                                  gsize                      count,
+                                  int                        io_priority,
+                                  GCancellable              *cancellable,
+                                  GAsyncReadyCallback        callback,
+                                  gpointer                   user_data)
+{
+  GSimpleAsyncResult *simple;
+  gssize nread;
+
+  nread =  g_memory_input_stream_read (stream, buffer, count, cancellable, NULL);
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                     callback,
+                                     user_data,
+                                     g_memory_input_stream_read_async);
+  g_simple_async_result_set_op_res_gssize (simple, nread);
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
+
+static gssize
+g_memory_input_stream_read_finish (GInputStream              *stream,
+                                  GAsyncResult              *result,
+                                  GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  gssize nread;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_memory_input_stream_read_async);
+  
+  nread = g_simple_async_result_get_op_res_gssize (simple);
+  return nread;
+}
+
+static void
+g_memory_input_stream_skip_async (GInputStream              *stream,
+                                  gsize                      count,
+                                  int                        io_priority,
+                                  GCancellable              *cancellable,
+                                  GAsyncReadyCallback        callback,
+                                  gpointer                   user_data)
+{
+  GSimpleAsyncResult *simple;
+  gssize nskipped;
+
+  nskipped = g_memory_input_stream_skip (stream, count, cancellable, NULL);
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                     callback,
+                                     user_data,
+                                     g_memory_input_stream_skip_async);
+  g_simple_async_result_set_op_res_gssize (simple, nskipped);
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
+
+static gssize
+g_memory_input_stream_skip_finish (GInputStream              *stream,
+                                  GAsyncResult              *result,
+                                  GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  gssize nskipped;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_memory_input_stream_skip_async);
+  
+  nskipped = g_simple_async_result_get_op_res_gssize (simple);
+  return nskipped;
+}
+
+static void
+g_memory_input_stream_close_async (GInputStream              *stream,
+                                   int                        io_priority,
+                                   GCancellable              *cancellable,
+                                   GAsyncReadyCallback        callback,
+                                   gpointer                   user_data)
+{
+  GSimpleAsyncResult *simple;
+  
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                     callback,
+                                     user_data,
+                                     g_memory_input_stream_close_async);
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
+
+static gboolean
+g_memory_input_stream_close_finish (GInputStream              *stream,
+                                   GAsyncResult              *result,
+                                   GError                   **error)
+{
+  return TRUE;
+}
+
+static goffset
+g_memory_input_stream_tell (GSeekable *seekable)
+{
+  GMemoryInputStream *memory_stream;
+  GMemoryInputStreamPrivate * priv;
+
+  memory_stream = G_MEMORY_INPUT_STREAM (seekable);
+  priv = memory_stream->priv;
+
+  return priv->pos;
+}
+
+static
+gboolean g_memory_input_stream_can_seek (GSeekable *seekable)
+{
+  return TRUE;
+}
+
+static gboolean
+g_memory_input_stream_seek (GSeekable       *seekable,
+                            goffset          offset,
+                            GSeekType        type,
+                            GCancellable    *cancellable,
+                            GError         **error)
+{
+  GMemoryInputStream *memory_stream;
+  GMemoryInputStreamPrivate * priv;
+  goffset absolute;
+
+  memory_stream = G_MEMORY_INPUT_STREAM (seekable);
+  priv = memory_stream->priv;
+
+  switch (type) {
+
+    case G_SEEK_CUR:
+      absolute = priv->pos + offset;
+      break;
+
+    case G_SEEK_SET:
+      absolute = offset;
+      break;
+
+    case G_SEEK_END:
+      absolute = priv->len + offset;
+      break;
+  
+    default:
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   "Invalid GSeekType supplied");
+
+      return FALSE;
+  }
+
+  if (absolute < 0 || absolute > priv->len)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   "Invalid seek request");
+      return FALSE;
+    }
+
+  priv->pos = absolute;
+
+  return TRUE;
+}
+
+static gboolean
+g_memory_input_stream_can_truncate (GSeekable *seekable)
+{
+  return FALSE;
+}
+
+static gboolean
+g_memory_input_stream_truncate (GSeekable      *seekable,
+                                goffset          offset,
+                                GCancellable    *cancellable,
+                                GError         **error)
+{
+  g_set_error (error,
+               G_IO_ERROR,
+               G_IO_ERROR_NOT_SUPPORTED,
+               "Cannot seek on GMemoryInputStream");
+  return FALSE;
+}
+
+/* vim: ts=2 sw=2 et */
+
diff --git a/gio/gmemoryinputstream.h b/gio/gmemoryinputstream.h
new file mode 100644 (file)
index 0000000..d62cb7f
--- /dev/null
@@ -0,0 +1,73 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#ifndef __G_MEMORY_INPUT_STREAM_H__
+#define __G_MEMORY_INPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/ginputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MEMORY_INPUT_STREAM         (g_memory_input_stream_get_type ())
+#define G_MEMORY_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MEMORY_INPUT_STREAM, GMemoryInputStream))
+#define G_MEMORY_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MEMORY_INPUT_STREAM, GMemoryInputStreamClass))
+#define G_IS_MEMORY_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MEMORY_INPUT_STREAM))
+#define G_IS_MEMORY_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MEMORY_INPUT_STREAM))
+#define G_MEMORY_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_MEMORY_INPUT_STREAM, GMemoryInputStreamClass))
+
+typedef struct _GMemoryInputStream         GMemoryInputStream;
+typedef struct _GMemoryInputStreamClass    GMemoryInputStreamClass;
+typedef struct _GMemoryInputStreamPrivate  GMemoryInputStreamPrivate;
+
+struct _GMemoryInputStream
+{
+  GInputStream parent;
+
+  /*< private >*/
+  GMemoryInputStreamPrivate *priv;
+};
+
+struct _GMemoryInputStreamClass
+{
+ GInputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+
+GType          g_memory_input_stream_get_type  (void) G_GNUC_CONST;
+GInputStream * g_memory_input_stream_from_data     (const void         *data,
+                                                   gssize              len);
+void           g_memory_input_stream_set_free_data (GMemoryInputStream *stream,
+                                                   gboolean            free_data);
+const void    *g_memory_input_stream_get_data      (GMemoryInputStream *stream);
+gsize          g_memory_input_stream_get_data_size (GMemoryInputStream *stream);
+
+G_END_DECLS
+
+#endif /* __G_MEMORY_INPUT_STREAM_H__ */
diff --git a/gio/gmemoryoutputstream.c b/gio/gmemoryoutputstream.c
new file mode 100644 (file)
index 0000000..966f34b
--- /dev/null
@@ -0,0 +1,678 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org>
+ */
+
+#include <config.h>
+#include "gmemoryoutputstream.h"
+#include "goutputstream.h"
+#include "gseekable.h"
+#include "gsimpleasyncresult.h"
+#include "string.h"
+#include "glibintl.h"
+
+struct _GMemoryOutputStreamPrivate {
+  
+  GByteArray *data;
+  goffset     pos;
+
+  guint       max_size;
+  guint       free_data : 1;
+};
+
+enum {
+  PROP_0,
+  PROP_DATA,
+  PROP_FREE_ARRAY,
+  PROP_SIZE_LIMIT
+};
+
+static void     g_memory_output_stream_finalize     (GObject         *object);
+
+static void     g_memory_output_stream_set_property (GObject      *object,
+                                                     guint         prop_id,
+                                                     const GValue *value,
+                                                     GParamSpec   *pspec);
+
+static void     g_memory_output_stream_get_property (GObject    *object,
+                                                     guint       prop_id,
+                                                     GValue     *value,
+                                                     GParamSpec *pspec);
+
+static gssize   g_memory_output_stream_write       (GOutputStream *stream,
+                                                    const void    *buffer,
+                                                    gsize          count,
+                                                    GCancellable  *cancellable,
+                                                    GError       **error);
+
+static gboolean g_memory_output_stream_close       (GOutputStream  *stream,
+                                                    GCancellable   *cancellable,
+                                                    GError        **error);
+
+static void     g_memory_output_stream_write_async  (GOutputStream        *stream,
+                                                     const void           *buffer,
+                                                     gsize                 count,
+                                                     int                   io_priority,
+                                                     GCancellable         *cancellable,
+                                                     GAsyncReadyCallback   callback,
+                                                     gpointer              data);
+static gssize   g_memory_output_stream_write_finish (GOutputStream        *stream,
+                                                     GAsyncResult         *result,
+                                                     GError              **error);
+static void     g_memory_output_stream_close_async  (GOutputStream        *stream,
+                                                     int                   io_priority,
+                                                     GCancellable         *cancellable,
+                                                     GAsyncReadyCallback   callback,
+                                                     gpointer              data);
+static gboolean g_memory_output_stream_close_finish (GOutputStream        *stream,
+                                                     GAsyncResult         *result,
+                                                     GError              **error);
+
+static void     g_memory_output_stream_seekable_iface_init (GSeekableIface  *iface);
+static goffset  g_memory_output_stream_tell                (GSeekable       *seekable);
+static gboolean g_memory_output_stream_can_seek            (GSeekable       *seekable);
+static gboolean g_memory_output_stream_seek                (GSeekable       *seekable,
+                                                           goffset          offset,
+                                                           GSeekType        type,
+                                                           GCancellable    *cancellable,
+                                                           GError         **error);
+static gboolean g_memory_output_stream_can_truncate        (GSeekable       *seekable);
+static gboolean g_memory_output_stream_truncate            (GSeekable       *seekable,
+                                                           goffset          offset,
+                                                           GCancellable    *cancellable,
+                                                           GError         **error);
+
+G_DEFINE_TYPE_WITH_CODE (GMemoryOutputStream, g_memory_output_stream, G_TYPE_OUTPUT_STREAM,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_SEEKABLE,
+                                                g_memory_output_stream_seekable_iface_init))
+
+
+static void
+g_memory_output_stream_class_init (GMemoryOutputStreamClass *klass)
+{
+  GOutputStreamClass *ostream_class;
+  GObjectClass *gobject_class;
+
+  g_type_class_add_private (klass, sizeof (GMemoryOutputStreamPrivate));
+
+  gobject_class = G_OBJECT_CLASS (klass);
+  gobject_class->finalize = g_memory_output_stream_finalize;
+  gobject_class->get_property = g_memory_output_stream_get_property;
+  gobject_class->set_property = g_memory_output_stream_set_property;
+
+  ostream_class = G_OUTPUT_STREAM_CLASS (klass);
+
+  ostream_class->write = g_memory_output_stream_write;
+  ostream_class->close = g_memory_output_stream_close;
+  ostream_class->write_async  = g_memory_output_stream_write_async;
+  ostream_class->write_finish = g_memory_output_stream_write_finish;
+  ostream_class->close_async  = g_memory_output_stream_close_async;
+  ostream_class->close_finish = g_memory_output_stream_close_finish;
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_DATA,
+                                   g_param_spec_pointer ("data",
+                                                         P_("Data byte array"),
+                                                         P_("The byte array used as internal storage."),
+                                                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT | 
+                                                         G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+  g_object_class_install_property (gobject_class,
+                                   PROP_FREE_ARRAY,
+                                   g_param_spec_boolean ("free-array",
+                                                         P_("Free array data"),
+                                                         P_("Wether or not the interal array should be free on close."),
+                                                         FALSE,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+  g_object_class_install_property (gobject_class,
+                                   PROP_SIZE_LIMIT,
+                                   g_param_spec_uint ("size-limit",
+                                                      P_("Limit"),
+                                                      P_("Maximum amount of bytes that can be written to the stream."),
+                                                      0,
+                                                      G_MAXUINT,
+                                                      0,
+                                                      G_PARAM_READWRITE |
+                                                      G_PARAM_STATIC_NAME|G_PARAM_STATIC_NICK|G_PARAM_STATIC_BLURB));
+
+
+}
+
+static void
+g_memory_output_stream_finalize (GObject *object)
+{
+  GMemoryOutputStream        *stream;
+
+  stream = G_MEMORY_OUTPUT_STREAM (object);
+  
+  if (stream->priv->free_data)
+    g_byte_array_free (stream->priv->data, TRUE);
+    
+  if (G_OBJECT_CLASS (g_memory_output_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_memory_output_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_memory_output_stream_seekable_iface_init (GSeekableIface *iface)
+{
+  iface->tell         = g_memory_output_stream_tell;
+  iface->can_seek     = g_memory_output_stream_can_seek;
+  iface->seek         = g_memory_output_stream_seek;
+  iface->can_truncate = g_memory_output_stream_can_truncate;
+  iface->truncate     = g_memory_output_stream_truncate;
+}
+
+
+static void
+g_memory_output_stream_init (GMemoryOutputStream *stream)
+{
+  GMemoryOutputStreamPrivate *priv;
+
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                              G_TYPE_MEMORY_OUTPUT_STREAM,
+                                              GMemoryOutputStreamPrivate);
+
+  priv = stream->priv;
+  priv->data = NULL; 
+}
+
+/**
+ * g_memory_output_stream_new:
+ * @data: a #GByteArray.
+ *
+ * Creates a new #GMemoryOutputStream. If @data is non-%NULL it will use
+ * that for its internal storage otherwise it will create a new #GByteArray.
+ * In both cases the internal #GByteArray can later be accessed through the 
+ * "data" property.
+ *
+ * Note: The new stream will not take ownership of the supplied
+ * @data so you have to free it yourself after use or explicitly
+ * ask for it be freed on close by setting the "free-array" 
+ * property to $TRUE.
+ *
+ * Return value: A newly created #GMemoryOutputStream object.
+ **/
+GOutputStream *
+g_memory_output_stream_new (GByteArray *data)
+{
+  GOutputStream *stream;
+
+  if (data == NULL) {
+    stream = g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM, NULL);
+  } else {
+    stream = g_object_new (G_TYPE_MEMORY_OUTPUT_STREAM,
+                           "data", data,
+                           NULL);
+  }
+
+  return stream;
+}
+
+/**
+ * g_memory_output_stream_set_free_data:
+ * @ostream:
+ * @free_data:
+ * 
+ **/
+void
+g_memory_output_stream_set_free_data (GMemoryOutputStream *ostream,
+                                     gboolean free_data)
+{
+  GMemoryOutputStreamPrivate *priv;
+
+  g_return_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream));
+
+  priv = ostream->priv;
+
+  priv->free_data = free_data;
+}
+
+/**
+ * g_memory_output_stream_set_max_size:
+ * @ostream:
+ * @max_size:
+ * 
+ **/
+void
+g_memory_output_stream_set_max_size (GMemoryOutputStream *ostream,
+                                     guint                max_size)
+{
+  GMemoryOutputStreamPrivate *priv;
+  
+  g_return_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream));
+
+  priv = ostream->priv;
+
+  priv->max_size = max_size;
+
+  if (priv->max_size > 0 &&
+      priv->max_size < priv->data->len) {
+
+    g_byte_array_set_size (priv->data, priv->max_size);
+
+    if (priv->pos > priv->max_size) {
+      priv->pos = priv->max_size;
+    }
+  }
+
+  g_object_notify (G_OBJECT (ostream), "size-limit");
+}
+
+static void
+g_memory_output_stream_set_property (GObject         *object,
+                                     guint            prop_id,
+                                     const GValue    *value,
+                                     GParamSpec      *pspec)
+{
+  GMemoryOutputStream *ostream;
+  GMemoryOutputStreamPrivate *priv;
+  GByteArray *data;
+  guint       max_size;
+
+  ostream = G_MEMORY_OUTPUT_STREAM (object);
+  priv = ostream->priv;
+
+  switch (prop_id)
+    {
+    case PROP_DATA:
+
+      if (priv->data && priv->free_data) {
+        g_byte_array_free (priv->data, TRUE);
+      }
+
+      data = g_value_get_pointer (value);
+
+      if (data == NULL) {
+        data = g_byte_array_new (); 
+        priv->free_data = TRUE;
+      } else {
+        priv->free_data = FALSE;
+      }
+      priv->data = data;
+      priv->pos  = 0;
+      g_object_notify (G_OBJECT (ostream), "data");
+      break;
+
+    case PROP_FREE_ARRAY:
+      priv->free_data = g_value_get_boolean (value);
+      break;
+
+    case PROP_SIZE_LIMIT:
+      max_size = g_value_get_uint (value);
+      g_memory_output_stream_set_max_size (ostream, max_size);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+g_memory_output_stream_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  GMemoryOutputStream *ostream;
+  GMemoryOutputStreamPrivate *priv;
+
+  ostream = G_MEMORY_OUTPUT_STREAM (object);
+  priv = ostream->priv;
+
+  switch (prop_id)
+    {
+    case PROP_DATA:
+      g_value_set_pointer (value, priv->data);
+      break;
+
+    case PROP_FREE_ARRAY:
+      g_value_set_boolean (value, priv->free_data);
+      break;
+
+    case PROP_SIZE_LIMIT:
+      g_value_set_uint (value, priv->max_size);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/**
+ * g_memory_output_stream_get_data:
+ * @ostream:
+ * 
+ * Returns: #GByteArray of the stream's data.
+ **/
+GByteArray *
+g_memory_output_stream_get_data (GMemoryOutputStream *ostream)
+{
+  g_return_val_if_fail (G_IS_MEMORY_OUTPUT_STREAM (ostream), NULL);
+
+  return ostream->priv->data;
+}
+
+
+static gboolean
+array_check_boundary (GMemoryOutputStream  *stream,
+                      goffset               size,
+                      GError              **error)
+{
+  GMemoryOutputStreamPrivate *priv;
+
+  priv = stream->priv;
+
+  if (! priv->max_size) {
+    return TRUE;
+  }
+
+  if (priv->max_size < size || size > G_MAXUINT) {
+
+    g_set_error (error,
+                 G_IO_ERROR,
+                 G_IO_ERROR_FAILED,
+                 "Reached maximum data array limit");
+
+    return FALSE;
+  }
+
+  return TRUE; 
+}
+
+static gssize
+array_resize (GMemoryOutputStream  *stream,
+              goffset               size,
+              GError              **error)
+{
+  GMemoryOutputStreamPrivate *priv;
+  guint old_len;
+
+  priv = stream->priv;
+
+  if (! array_check_boundary (stream, size, error)) 
+    return -1;
+  
+
+  if (priv->data->len == size) 
+    return priv->data->len - priv->pos;
+  
+
+  old_len = priv->data->len;
+  g_byte_array_set_size (priv->data, size);
+
+  if (size > old_len && priv->pos > old_len) 
+    memset (priv->data->data + priv->pos, 0, size - old_len);
+  
+
+  return priv->data->len - priv->pos;
+}
+
+static gssize
+g_memory_output_stream_write (GOutputStream *stream,
+                              const void    *buffer,
+                              gsize          count,
+                              GCancellable  *cancellable,
+                              GError       **error)
+{
+  GMemoryOutputStream        *ostream;
+  GMemoryOutputStreamPrivate *priv;
+  gsize     new_size;
+  gssize    n;
+  guint8   *dest;
+
+  ostream = G_MEMORY_OUTPUT_STREAM (stream);
+  priv = ostream->priv;
+
+  /* count < 0 is ensured by GOutputStream */
+
+  n = MIN (count, priv->data->len - priv->pos);
+
+  if (n < 1)
+    {
+      new_size = priv->pos + count;
+
+      if (priv->max_size > 0)
+        {
+          new_size = MIN (new_size, priv->max_size);
+        }
+
+      n = array_resize (ostream, new_size, error);
+
+      if (n == 0) 
+        {
+          g_set_error (error,
+                       G_IO_ERROR,
+                       G_IO_ERROR_FAILED,
+                       "Reached maximum data array limit");
+          return -1;
+        }
+      else if (n < 0)
+        {
+          return -1;
+        }
+    }
+
+  dest = priv->data->data + priv->pos;
+  memcpy (dest, buffer, n); 
+  priv->pos += n;
+
+  return n;
+}
+
+static gboolean
+g_memory_output_stream_close (GOutputStream  *stream,
+                              GCancellable   *cancellable,
+                              GError        **error)
+{
+  GMemoryOutputStream        *ostream;
+  GMemoryOutputStreamPrivate *priv;
+
+  ostream = G_MEMORY_OUTPUT_STREAM (stream);
+  priv = ostream->priv;
+
+  return TRUE;
+}
+
+static void
+g_memory_output_stream_write_async  (GOutputStream        *stream,
+                                     const void           *buffer,
+                                     gsize                 count,
+                                     int                   io_priority,
+                                     GCancellable         *cancellable,
+                                     GAsyncReadyCallback   callback,
+                                     gpointer              data)
+{
+  GSimpleAsyncResult *simple;
+  gssize nwritten;
+
+  nwritten = g_memory_output_stream_write (stream,
+                                           buffer,
+                                           count,
+                                           cancellable,
+                                           NULL);
+
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                      callback,
+                                      data,
+                                      g_memory_output_stream_write_async);
+  
+  g_simple_async_result_set_op_res_gssize (simple, nwritten); 
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+
+}
+
+static gssize
+g_memory_output_stream_write_finish (GOutputStream        *stream,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+  GSimpleAsyncResult *simple;
+  gssize nwritten;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+            g_memory_output_stream_write_async);
+
+  nwritten = g_simple_async_result_get_op_res_gssize (simple);
+  return nwritten;
+}
+
+static void
+g_memory_output_stream_close_async  (GOutputStream        *stream,
+                                     int                   io_priority,
+                                     GCancellable         *cancellable,
+                                     GAsyncReadyCallback   callback,
+                                     gpointer              data)
+{
+  GSimpleAsyncResult *simple;
+
+  simple = g_simple_async_result_new (G_OBJECT (stream),
+                                      callback,
+                                      data,
+                                      g_memory_output_stream_close_async);
+
+
+  /* will always return TRUE */
+  g_memory_output_stream_close (stream, cancellable, NULL);
+  
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+
+}
+
+static gboolean
+g_memory_output_stream_close_finish (GOutputStream        *stream,
+                                     GAsyncResult         *result,
+                                     GError              **error)
+{
+  GSimpleAsyncResult *simple;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == 
+            g_memory_output_stream_close_async);
+
+
+  return TRUE;
+}
+
+static goffset
+g_memory_output_stream_tell (GSeekable  *seekable)
+{
+  GMemoryOutputStream *stream;
+  GMemoryOutputStreamPrivate *priv;
+
+  stream = G_MEMORY_OUTPUT_STREAM (seekable);
+  priv = stream->priv;
+
+  return priv->pos;
+}
+
+static gboolean
+g_memory_output_stream_can_seek (GSeekable *seekable)
+{
+  return TRUE;
+}
+
+static gboolean
+g_memory_output_stream_seek (GSeekable      *seekable,
+                            goffset          offset,
+                            GSeekType        type,
+                            GCancellable    *cancellable,
+                            GError         **error)
+{
+  GMemoryOutputStream        *stream;
+  GMemoryOutputStreamPrivate *priv;
+  goffset absolute;
+
+  stream = G_MEMORY_OUTPUT_STREAM (seekable);
+  priv = stream->priv;
+
+  switch (type) {
+
+    case G_SEEK_CUR:
+      absolute = priv->pos + offset;
+      break;
+
+    case G_SEEK_SET:
+      absolute = offset;
+      break;
+
+    case G_SEEK_END:
+      absolute = priv->data->len + offset;
+      break;
+  
+    default:
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_ARGUMENT,
+                   "Invalid GSeekType supplied");
+
+      return FALSE;
+  }
+
+  if (absolute < 0) {
+    g_set_error (error,
+                 G_IO_ERROR,
+                 G_IO_ERROR_INVALID_ARGUMENT,
+                 "Invalid seek request");
+    return FALSE;
+  }
+
+  if (! array_check_boundary (stream, absolute, error)) 
+    return FALSE;  
+
+  priv->pos = absolute;
+
+  return TRUE;
+}
+
+static gboolean
+g_memory_output_stream_can_truncate (GSeekable *seekable)
+{
+  return TRUE;
+}
+
+static gboolean
+g_memory_output_stream_truncate (GSeekable      *seekable,
+                                 goffset         offset,
+                                 GCancellable   *cancellable,
+                                 GError         **error)
+{
+  GMemoryOutputStream *ostream;
+  GMemoryOutputStreamPrivate *priv;
+
+  ostream = G_MEMORY_OUTPUT_STREAM (seekable);
+  priv = ostream->priv;
+  if (array_resize (ostream, offset, error) < 0)
+      return FALSE;
+
+  return TRUE;
+}
+
+/* vim: ts=2 sw=2 et */
diff --git a/gio/gmemoryoutputstream.h b/gio/gmemoryoutputstream.h
new file mode 100644 (file)
index 0000000..bcb3cc7
--- /dev/null
@@ -0,0 +1,73 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Christian Kellner <gicmo@gnome.org> 
+ */
+
+#ifndef __G_MEMORY_OUTPUT_STREAM_H__
+#define __G_MEMORY_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/goutputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MEMORY_OUTPUT_STREAM         (g_memory_output_stream_get_type ())
+#define G_MEMORY_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MEMORY_OUTPUT_STREAM, GMemoryOutputStream))
+#define G_MEMORY_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MEMORY_OUTPUT_STREAM, GMemoryOutputStreamClass))
+#define G_IS_MEMORY_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MEMORY_OUTPUT_STREAM))
+#define G_IS_MEMORY_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MEMORY_OUTPUT_STREAM))
+#define G_MEMORY_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_MEMORY_OUTPUT_STREAM, GMemoryOutputStreamClass))
+
+typedef struct _GMemoryOutputStream         GMemoryOutputStream;
+typedef struct _GMemoryOutputStreamClass    GMemoryOutputStreamClass;
+typedef struct _GMemoryOutputStreamPrivate  GMemoryOutputStreamPrivate;
+
+struct _GMemoryOutputStream
+{
+  GOutputStream parent;
+
+  /*< private >*/
+  GMemoryOutputStreamPrivate *priv;
+};
+
+struct _GMemoryOutputStreamClass
+{
+ GOutputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+
+GType          g_memory_output_stream_get_type          (void) G_GNUC_CONST;
+GOutputStream *g_memory_output_stream_new               (GByteArray          *data);
+void           g_memory_output_stream_set_max_size      (GMemoryOutputStream *ostream,
+                                                        guint                max_size);
+GByteArray *   g_memory_output_stream_get_data          (GMemoryOutputStream *ostream);
+void           g_memory_output_stream_set_free_data     (GMemoryOutputStream *ostream,
+                                                        gboolean             free_data);
+
+G_END_DECLS
+
+#endif /* __G_MEMORY_OUTPUT_STREAM_H__ */
diff --git a/gio/gmountoperation.c b/gio/gmountoperation.c
new file mode 100644 (file)
index 0000000..034854b
--- /dev/null
@@ -0,0 +1,348 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "gmountoperation.h"
+#include "gio-marshal.h"
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GMountOperation, g_mount_operation, G_TYPE_OBJECT);
+
+enum {
+  ASK_PASSWORD,
+  ASK_QUESTION,
+  REPLY,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+struct _GMountOperationPrivate {
+  char *password;
+  char *user;
+  char *domain;
+  gboolean anonymous;
+  GPasswordSave password_save;
+  int choice;
+};
+
+static void
+g_mount_operation_finalize (GObject *object)
+{
+  GMountOperation *operation;
+  GMountOperationPrivate *priv;
+
+  operation = G_MOUNT_OPERATION (object);
+
+  priv = operation->priv;
+  
+  g_free (priv->password);
+  g_free (priv->user);
+  g_free (priv->domain);
+  
+  if (G_OBJECT_CLASS (g_mount_operation_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_mount_operation_parent_class)->finalize) (object);
+}
+
+static gboolean
+boolean_handled_accumulator (GSignalInvocationHint *ihint,
+                            GValue                *return_accu,
+                            const GValue          *handler_return,
+                            gpointer               dummy)
+{
+  gboolean continue_emission;
+  gboolean signal_handled;
+  
+  signal_handled = g_value_get_boolean (handler_return);
+  g_value_set_boolean (return_accu, signal_handled);
+  continue_emission = !signal_handled;
+  
+  return continue_emission;
+}
+
+static gboolean
+ask_password (GMountOperation *op,
+             const char      *message,
+             const char      *default_user,
+             const char      *default_domain,
+             GPasswordFlags   flags)
+{
+  return FALSE;
+}
+  
+static gboolean
+ask_question (GMountOperation *op,
+             const char      *message,
+             const char      *choices[])
+{
+  return FALSE;
+}
+
+static void
+g_mount_operation_class_init (GMountOperationClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GMountOperationPrivate));
+  
+  gobject_class->finalize = g_mount_operation_finalize;
+  
+  klass->ask_password = ask_password;
+  klass->ask_question = ask_question;
+  
+  signals[ASK_PASSWORD] =
+    g_signal_new (I_("ask_password"),
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GMountOperationClass, ask_password),
+                 boolean_handled_accumulator, NULL,
+                 _gio_marshal_BOOLEAN__STRING_STRING_STRING_INT,
+                 G_TYPE_BOOLEAN, 4,
+                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT);
+  
+  signals[ASK_QUESTION] =
+    g_signal_new (I_("ask_question"),
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GMountOperationClass, ask_question),
+                 boolean_handled_accumulator, NULL,
+                 _gio_marshal_BOOLEAN__STRING_POINTER,
+                 G_TYPE_BOOLEAN, 2,
+                 G_TYPE_STRING, G_TYPE_POINTER);
+
+  signals[REPLY] =
+    g_signal_new (I_("reply"),
+                 G_TYPE_FROM_CLASS (gobject_class),
+                 G_SIGNAL_RUN_LAST,
+                 G_STRUCT_OFFSET (GMountOperationClass, reply),
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__BOOLEAN,
+                 G_TYPE_NONE, 1,
+                 G_TYPE_BOOLEAN);
+}
+
+static void
+g_mount_operation_init (GMountOperation *operation)
+{
+  operation->priv = G_TYPE_INSTANCE_GET_PRIVATE (operation,
+                                                G_TYPE_MOUNT_OPERATION,
+                                                GMountOperationPrivate);
+}
+
+/**
+ * g_mount_operation_new:
+ * 
+ * Returns: a new #GMountOperation.
+ **/
+GMountOperation *
+g_mount_operation_new (void)
+{
+  return g_object_new (G_TYPE_MOUNT_OPERATION, NULL);
+}
+
+/**
+ * g_mount_operation_get_username
+ * @op:
+ * 
+ * Returns: 
+ **/
+const char *
+g_mount_operation_get_username (GMountOperation *op)
+{
+  g_return_val_if_fail (G_IS_MOUNT_OPERATION (op), NULL);
+  return op->priv->user;
+}
+
+/**
+ * g_mount_operation_set_username:
+ * @op:
+ * @username: input username.
+ * 
+ **/
+void
+g_mount_operation_set_username (GMountOperation *op,
+                               const char      *username)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  g_free (op->priv->user);
+  op->priv->user = g_strdup (username);
+}
+
+/**
+ * g_mount_operation_get_password:
+ * @op:
+ * 
+ * Returns:  
+ **/
+const char *
+g_mount_operation_get_password (GMountOperation *op)
+{
+  g_return_val_if_fail (G_IS_MOUNT_OPERATION (op), NULL);
+  return op->priv->password;
+}
+
+/**
+ * g_mount_operation_set_password:
+ * @op: the given #GMountOperation.
+ * @password: password to set.
+ * 
+ * Sets the mount operation's password to @password.  
+ *
+ **/
+void
+g_mount_operation_set_password (GMountOperation *op,
+                               const char      *password)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  g_free (op->priv->password);
+  op->priv->password = g_strdup (password);
+}
+
+/**
+ * g_mount_operation_get_anonymous:
+ * @op:
+ * 
+ * Returns: %TRUE if mount operation is anonymous. 
+ **/
+gboolean
+g_mount_operation_get_anonymous (GMountOperation *op)
+{
+  g_return_val_if_fail (G_IS_MOUNT_OPERATION (op), FALSE);
+  return op->priv->anonymous;
+}
+
+/**
+ * g_mount_operation_set_anonymous:
+ * @op: the given #GMountOperation.
+ * @anonymous: boolean value.
+ * 
+ **/  
+void
+g_mount_operation_set_anonymous (GMountOperation *op,
+                                gboolean         anonymous)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  op->priv->anonymous = anonymous;
+}
+
+/**
+ * g_mount_operation_get_domain:
+ * @op:
+ * 
+ * Returns: a const string set to the domain.
+ **/
+const char *
+g_mount_operation_get_domain (GMountOperation *op)
+{
+  g_return_val_if_fail (G_IS_MOUNT_OPERATION (op), NULL);
+  return op->priv->domain;
+}
+
+/**
+ * g_mount_operation_set_domain:
+ * @op: the given #GMountOperation.
+ * @domain: the domain to set.
+ * 
+ * Sets the mount operation's domain. 
+ **/  
+void
+g_mount_operation_set_domain (GMountOperation *op,
+                             const char      *domain)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  g_free (op->priv->domain);
+  op->priv->domain = g_strdup (domain);
+}
+
+/**
+ * g_mount_operation_get_password_save:
+ * @op: the given #GMountOperation.
+ *
+ * Returns: #GPasswordSave. 
+ **/  
+
+GPasswordSave
+g_mount_operation_get_password_save (GMountOperation *op)
+{
+  g_return_val_if_fail (G_IS_MOUNT_OPERATION (op), G_PASSWORD_SAVE_NEVER);
+  return op->priv->password_save;
+}
+
+/**
+ * g_mount_operation_set_password_save
+ * @op:
+ * @save: #GPasswordSave
+ * 
+ **/   
+void
+g_mount_operation_set_password_save (GMountOperation *op,
+                                    GPasswordSave    save)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  op->priv->password_save = save;
+}
+
+/**
+ * g_mount_operation_get_choice:
+ * @op:
+ * 
+ * Returns: 
+ **/
+int
+g_mount_operation_get_choice (GMountOperation *op)
+{
+  g_return_val_if_fail (G_IS_MOUNT_OPERATION (op), 0);
+  return op->priv->choice;
+}
+
+/**
+ * g_mount_operation_set_choice:
+ * @op:
+ * @choice:
+ *  
+ **/
+void
+g_mount_operation_set_choice (GMountOperation *op,
+                             int choice)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  op->priv->choice = choice;
+}
+
+/**
+ * g_mount_operation_reply:
+ * @op: #GMountOperation.
+ * @abort: boolean.
+ * 
+ * Emits the #GMountOperation::Reply signal with the abort flag set to
+ * @abort.
+ **/
+void
+g_mount_operation_reply (GMountOperation *op,
+                        gboolean         abort)
+{
+  g_return_if_fail (G_IS_MOUNT_OPERATION (op));
+  g_signal_emit (op, signals[REPLY], 0, abort);
+}
diff --git a/gio/gmountoperation.h b/gio/gmountoperation.h
new file mode 100644 (file)
index 0000000..3b730b5
--- /dev/null
@@ -0,0 +1,126 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_MOUNT_OPERATION_H__
+#define __G_MOUNT_OPERATION_H__
+
+#include <sys/stat.h>
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_MOUNT_OPERATION         (g_mount_operation_get_type ())
+#define G_MOUNT_OPERATION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_MOUNT_OPERATION, GMountOperation))
+#define G_MOUNT_OPERATION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_MOUNT_OPERATION, GMountOperationClass))
+#define G_IS_MOUNT_OPERATION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_MOUNT_OPERATION))
+#define G_IS_MOUNT_OPERATION_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_MOUNT_OPERATION))
+#define G_MOUNT_OPERATION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_MOUNT_OPERATION, GMountOperationClass))
+
+typedef struct _GMountOperation        GMountOperation;
+typedef struct _GMountOperationClass   GMountOperationClass;
+typedef struct _GMountOperationPrivate GMountOperationPrivate;
+
+struct _GMountOperation
+{
+  GObject parent_instance;
+
+  GMountOperationPrivate *priv;
+};
+
+typedef enum {
+  G_PASSWORD_FLAGS_NEED_PASSWORD    = 1<<0,
+  G_PASSWORD_FLAGS_NEED_USERNAME    = 1<<1,
+  G_PASSWORD_FLAGS_NEED_DOMAIN      = 1<<2,
+  G_PASSWORD_FLAGS_SAVING_SUPPORTED = 1<<4,
+  G_PASSWORD_FLAGS_ANON_SUPPORTED   = 1<<5
+} GPasswordFlags;
+
+typedef enum {
+  G_PASSWORD_SAVE_NEVER,
+  G_PASSWORD_SAVE_FOR_SESSION,
+  G_PASSWORD_SAVE_PERMANENTLY
+} GPasswordSave;
+
+struct _GMountOperationClass
+{
+  GObjectClass parent_class;
+
+  /* signals: */
+
+  gboolean (* ask_password) (GMountOperation *op,
+                            const char      *message,
+                            const char      *default_user,
+                            const char      *default_domain,
+                            GPasswordFlags   flags);
+  
+  gboolean (* ask_question) (GMountOperation *op,
+                            const char      *message,
+                            const char      *choices[]);
+
+  void     (* reply)        (GMountOperation *op,
+                            gboolean         abort);
+  
+  
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+  void (*_g_reserved9) (void);
+  void (*_g_reserved10) (void);
+  void (*_g_reserved11) (void);
+  void (*_g_reserved12) (void);
+};
+
+GType g_mount_operation_get_type (void) G_GNUC_CONST;
+  
+GMountOperation *  g_mount_operation_new (void);
+
+const char *  g_mount_operation_get_username      (GMountOperation *op);
+void          g_mount_operation_set_username      (GMountOperation *op,
+                                                  const char      *username);
+const char *  g_mount_operation_get_password      (GMountOperation *op);
+void          g_mount_operation_set_password      (GMountOperation *op,
+                                                  const char      *password);
+gboolean      g_mount_operation_get_anonymous     (GMountOperation *op);
+void          g_mount_operation_set_anonymous     (GMountOperation *op,
+                                                  gboolean         anonymous);
+const char *  g_mount_operation_get_domain        (GMountOperation *op);
+void          g_mount_operation_set_domain        (GMountOperation *op,
+                                                  const char      *domain);
+GPasswordSave g_mount_operation_get_password_save (GMountOperation *op);
+void          g_mount_operation_set_password_save (GMountOperation *op,
+                                                  GPasswordSave    save);
+int           g_mount_operation_get_choice        (GMountOperation *op);
+void          g_mount_operation_set_choice        (GMountOperation *op,
+                                                  int              choice);
+void          g_mount_operation_reply             (GMountOperation *op,
+                                                  gboolean         abort);
+
+G_END_DECLS
+
+#endif /* __G_MOUNT_OPERATION_H__ */
diff --git a/gio/gnativevolumemonitor.c b/gio/gnativevolumemonitor.c
new file mode 100644 (file)
index 0000000..cc2d81a
--- /dev/null
@@ -0,0 +1,31 @@
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include "gnativevolumemonitor.h"
+
+
+G_DEFINE_ABSTRACT_TYPE (GNativeVolumeMonitor, g_native_volume_monitor, G_TYPE_VOLUME_MONITOR);
+
+static void
+g_native_volume_monitor_finalize (GObject *object)
+{
+  if (G_OBJECT_CLASS (g_native_volume_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_native_volume_monitor_parent_class)->finalize) (object);
+}
+
+
+static void
+g_native_volume_monitor_class_init (GNativeVolumeMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_native_volume_monitor_finalize;
+}
+
+
+static void
+g_native_volume_monitor_init (GNativeVolumeMonitor *native_monitor)
+{
+}
diff --git a/gio/gnativevolumemonitor.h b/gio/gnativevolumemonitor.h
new file mode 100644 (file)
index 0000000..c3ccca2
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef __G_NATIVE_VOLUME_MONITOR_H__
+#define __G_NATIVE_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gvolumemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_NATIVE_VOLUME_MONITOR        (g_native_volume_monitor_get_type ())
+#define G_NATIVE_VOLUME_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_NATIVE_VOLUME_MONITOR, GNativeVolumeMonitor))
+#define G_NATIVE_VOLUME_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_NATIVE_VOLUME_MONITOR, GNativeVolumeMonitorClass))
+#define G_IS_NATIVE_VOLUME_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_NATIVE_VOLUME_MONITOR))
+#define G_IS_NATIVE_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_NATIVE_VOLUME_MONITOR))
+
+typedef struct _GNativeVolumeMonitor GNativeVolumeMonitor;
+typedef struct _GNativeVolumeMonitorClass GNativeVolumeMonitorClass;
+
+struct _GNativeVolumeMonitor {
+  GVolumeMonitor parent;
+};
+
+struct _GNativeVolumeMonitorClass {
+  GVolumeMonitorClass parent_class;
+
+  GVolume * (*get_volume_for_mountpoint) (const char *mountpoint);
+  
+  int priority;
+};
+
+GType g_native_volume_monitor_get_type (void) G_GNUC_CONST;
+
+
+G_END_DECLS
+
+#endif /* __G_NATIVE_VOLUME_MONITOR_H__ */
diff --git a/gio/goutputstream.c b/gio/goutputstream.c
new file mode 100644 (file)
index 0000000..514ea87
--- /dev/null
@@ -0,0 +1,1320 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "goutputstream.h"
+#include "gsimpleasyncresult.h"
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GOutputStream, g_output_stream, G_TYPE_OBJECT);
+
+struct _GOutputStreamPrivate {
+  guint closed : 1;
+  guint pending : 1;
+  guint cancelled : 1;
+  GAsyncReadyCallback outstanding_callback;
+};
+
+static gssize   g_output_stream_real_splice        (GOutputStream             *stream,
+                                                   GInputStream              *source,
+                                                   GOutputStreamSpliceFlags   flags,
+                                                   GCancellable              *cancellable,
+                                                   GError                   **error);
+static void     g_output_stream_real_write_async   (GOutputStream             *stream,
+                                                   const void                *buffer,
+                                                   gsize                      count,
+                                                   int                        io_priority,
+                                                   GCancellable              *cancellable,
+                                                   GAsyncReadyCallback        callback,
+                                                   gpointer                   data);
+static gssize   g_output_stream_real_write_finish  (GOutputStream             *stream,
+                                                   GAsyncResult              *result,
+                                                   GError                   **error);
+static void     g_output_stream_real_splice_async  (GOutputStream             *stream,
+                                                   GInputStream              *source,
+                                                   GOutputStreamSpliceFlags   flags,
+                                                   int                        io_priority,
+                                                   GCancellable              *cancellable,
+                                                   GAsyncReadyCallback        callback,
+                                                   gpointer                   data);
+static gssize   g_output_stream_real_splice_finish (GOutputStream             *stream,
+                                                   GAsyncResult              *result,
+                                                   GError                   **error);
+static void     g_output_stream_real_flush_async   (GOutputStream             *stream,
+                                                   int                        io_priority,
+                                                   GCancellable              *cancellable,
+                                                   GAsyncReadyCallback        callback,
+                                                   gpointer                   data);
+static gboolean g_output_stream_real_flush_finish  (GOutputStream             *stream,
+                                                   GAsyncResult              *result,
+                                                   GError                   **error);
+static void     g_output_stream_real_close_async   (GOutputStream             *stream,
+                                                   int                        io_priority,
+                                                   GCancellable              *cancellable,
+                                                   GAsyncReadyCallback        callback,
+                                                   gpointer                   data);
+static gboolean g_output_stream_real_close_finish  (GOutputStream             *stream,
+                                                   GAsyncResult              *result,
+                                                   GError                   **error);
+
+static void
+g_output_stream_finalize (GObject *object)
+{
+  GOutputStream *stream;
+
+  stream = G_OUTPUT_STREAM (object);
+  
+  if (G_OBJECT_CLASS (g_output_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_output_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_output_stream_dispose (GObject *object)
+{
+  GOutputStream *stream;
+
+  stream = G_OUTPUT_STREAM (object);
+  
+  if (!stream->priv->closed)
+    g_output_stream_close (stream, NULL, NULL);
+  
+  if (G_OBJECT_CLASS (g_output_stream_parent_class)->dispose)
+    (*G_OBJECT_CLASS (g_output_stream_parent_class)->dispose) (object);
+}
+
+static void
+g_output_stream_class_init (GOutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GOutputStreamPrivate));
+  
+  gobject_class->finalize = g_output_stream_finalize;
+  gobject_class->dispose = g_output_stream_dispose;
+
+  klass->splice = g_output_stream_real_splice;
+  
+  klass->write_async = g_output_stream_real_write_async;
+  klass->write_finish = g_output_stream_real_write_finish;
+  klass->splice_async = g_output_stream_real_splice_async;
+  klass->splice_finish = g_output_stream_real_splice_finish;
+  klass->flush_async = g_output_stream_real_flush_async;
+  klass->flush_finish = g_output_stream_real_flush_finish;
+  klass->close_async = g_output_stream_real_close_async;
+  klass->close_finish = g_output_stream_real_close_finish;
+}
+
+static void
+g_output_stream_init (GOutputStream *stream)
+{
+  stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+                                             G_TYPE_OUTPUT_STREAM,
+                                             GOutputStreamPrivate);
+}
+
+/**
+ * g_output_stream_write:
+ * @stream: a #GOutputStream.
+ * @buffer: the buffer containing the data to write. 
+ * @count: the number of bytes to write
+ * @cancellable: optional cancellable object
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Tries to write @count bytes from @buffer into the stream. Will block
+ * during the operation.
+ * 
+ * If count is zero returns zero and does nothing. A value of @count
+ * larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
+ *
+ * On success, the number of bytes written to the stream is returned.
+ * It is not an error if this is not the same as the requested size, as it
+ * can happen e.g. on a partial i/o error, or if the there is not enough
+ * storage in the stream. All writes either block until at least one byte
+ * is written, so zero is never returned (unless @count is zero).
+ * 
+ * If @cancellable is not NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error G_IO_ERROR_CANCELLED will be returned. If an
+ * operation was partially finished when the operation was cancelled the
+ * partial result will be returned, without an error.
+ *
+ * On error -1 is returned and @error is set accordingly.
+ * 
+ * Return value: Number of bytes written, or -1 on error
+ **/
+gssize
+g_output_stream_write (GOutputStream *stream,
+                      const void    *buffer,
+                      gsize          count,
+                      GCancellable  *cancellable,
+                      GError       **error)
+{
+  GOutputStreamClass *class;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), -1);
+  g_return_val_if_fail (buffer != NULL, 0);
+
+  if (count == 0)
+    return 0;
+  
+  if (((gssize) count) < 0)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                  _("Too large count value passed to g_output_stream_write"));
+      return -1;
+    }
+
+  if (stream->priv->closed)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return -1;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return -1;
+    }
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+
+  if (class->write == NULL) 
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                  _("Output stream doesn't implement write"));
+      return -1;
+    }
+  
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+  
+  stream->priv->pending = TRUE;
+  res = class->write (stream, buffer, count, cancellable, error);
+  stream->priv->pending = FALSE;
+  
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  return res; 
+}
+
+/**
+ * g_output_stream_write_all:
+ * @stream: a #GOutputStream.
+ * @buffer: the buffer containing the data to write. 
+ * @count: the number of bytes to write
+ * @bytes_written: location to store the number of bytes that was written to the stream
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Tries to write @count bytes from @buffer into the stream. Will block
+ * during the operation.
+ * 
+ * This function is similar to g_output_stream_write(), except it tries to
+ * write as many bytes as requested, only stopping on an error.
+ *
+ * On a successful write of @count bytes, %TRUE is returned, and @bytes_written
+ * is set to @count.
+ * 
+ * If there is an error during the operation FALSE is returned and @error
+ * is set to indicate the error status, @bytes_written is updated to contain
+ * the number of bytes written into the stream before the error occured.
+ *
+ * Return value: %TRUE on success, %FALSE if there was an error
+ **/
+gboolean
+g_output_stream_write_all (GOutputStream *stream,
+                          const void    *buffer,
+                          gsize          count,
+                          gsize         *bytes_written,
+                          GCancellable  *cancellable,
+                          GError       **error)
+{
+  gsize _bytes_written;
+  gssize res;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (buffer != NULL, FALSE);
+
+  _bytes_written = 0;
+  while (_bytes_written < count)
+    {
+      res = g_output_stream_write (stream, (char *)buffer + _bytes_written, count - _bytes_written,
+                                  cancellable, error);
+      if (res == -1)
+       {
+         if (bytes_written)
+           *bytes_written = _bytes_written;
+         return FALSE;
+       }
+      
+      if (res == 0)
+       g_warning ("Write returned zero without error");
+
+      _bytes_written += res;
+    }
+  
+  if (bytes_written)
+    *bytes_written = _bytes_written;
+  return TRUE;
+}
+
+/**
+ * g_output_stream_flush:
+ * @stream: a #GOutputStream.
+ * @cancellable: optional cancellable object
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Flushed any outstanding buffers in the stream. Will block during the operation.
+ * Closing the stream will implicitly cause a flush.
+ *
+ * This function is optional for inherited classes.
+ * 
+ * If @cancellable is not NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error G_IO_ERROR_CANCELLED will be returned.
+ *
+ * Return value: TRUE on success, FALSE on error
+ **/
+gboolean
+g_output_stream_flush (GOutputStream    *stream,
+                      GCancellable  *cancellable,
+                      GError          **error)
+{
+  GOutputStreamClass *class;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+
+  if (stream->priv->closed)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Stream is already closed"));
+      return FALSE;
+    }
+
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return FALSE;
+    }
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+
+  res = TRUE;
+  if (class->flush)
+    {
+      if (cancellable)
+       g_push_current_cancellable (cancellable);
+      
+      stream->priv->pending = TRUE;
+      res = class->flush (stream, cancellable, error);
+      stream->priv->pending = FALSE;
+      
+      if (cancellable)
+       g_pop_current_cancellable (cancellable);
+    }
+  
+  return res;
+}
+
+/**
+ * g_output_stream_splice:
+ * @stream:
+ * @source:
+ * @flags:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ *
+ *
+ * Returns: 
+ **/
+gssize
+g_output_stream_splice (GOutputStream *stream,
+                       GInputStream *source,
+                       GOutputStreamSpliceFlags flags,
+                       GCancellable  *cancellable,
+                       GError **error)
+{
+  GOutputStreamClass *class;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), -1);
+  g_return_val_if_fail (G_IS_INPUT_STREAM (source), -1);
+
+  if (stream->priv->closed)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Target stream is already closed"));
+      return -1;
+    }
+
+  if (g_input_stream_is_closed (source))
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_CLOSED,
+                  _("Source stream is already closed"));
+      return -1;
+    }
+
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return -1;
+    }
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+
+  res = TRUE;
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+      
+  stream->priv->pending = TRUE;
+  res = class->splice (stream, source, flags, cancellable, error);
+  stream->priv->pending = FALSE;
+      
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  return res;
+}
+
+static gssize
+g_output_stream_real_splice (GOutputStream *stream,
+                            GInputStream *source,
+                            GOutputStreamSpliceFlags flags,
+                            GCancellable  *cancellable,
+                            GError **error)
+{
+  gssize n_read, n_written;
+  gssize bytes_copied;
+  char buffer[8192], *p;
+  gboolean res;
+
+  bytes_copied = 0;
+  res = TRUE;
+  do 
+    {
+      n_read = g_input_stream_read (source, buffer, sizeof (buffer), cancellable, error);
+      if (n_read == -1)
+       {
+         res = FALSE;
+         break;
+       }
+       
+      if (n_read == 0)
+       break;
+
+      p = buffer;
+      while (n_read > 0)
+       {
+         stream->priv->pending = FALSE;
+         n_written = g_output_stream_write (stream, p, n_read, cancellable, error);
+         stream->priv->pending = TRUE;
+         if (n_written == -1)
+           {
+             res = FALSE;
+             break;
+           }
+
+         p += n_written;
+         n_read -= n_written;
+         bytes_copied += n_written;
+       }
+    }
+  while (res);
+
+  if (!res)
+    error = NULL; /* Ignore further errors */
+
+  if (flags & G_OUTPUT_STREAM_SPLICE_FLAGS_CLOSE_SOURCE)
+    {
+      /* Don't care about errors in source here */
+      g_input_stream_close (source, cancellable, NULL);
+    }
+
+  if (flags & G_OUTPUT_STREAM_SPLICE_FLAGS_CLOSE_TARGET)
+    {
+      /* But write errors on close are bad! */
+      stream->priv->pending = FALSE;
+      if (!g_output_stream_close (stream, cancellable, error))
+       res = FALSE;
+      stream->priv->pending = TRUE;
+    }
+
+  if (res)
+    return bytes_copied;
+  
+  return -1;
+}
+
+
+/**
+ * g_output_stream_close:
+ * @stream: A #GOutputStream.
+ * @cancellable: optional cancellable object
+ * @error: location to store the error occuring, or %NULL to ignore
+ *
+ * Closes the stream, releasing resources related to it.
+ *
+ * Once the stream is closed, all other operations will return %G_IO_ERROR_CLOSED.
+ * Closing a stream multiple times will not return an error.
+ *
+ * Closing a stream will automatically flush any outstanding buffers in the
+ * stream.
+ *
+ * Streams will be automatically closed when the last reference
+ * is dropped, but you might want to call make sure resources
+ * are released as early as possible.
+ *
+ * Some streams might keep the backing store of the stream (e.g. a file descriptor)
+ * open after the stream is closed. See the documentation for the individual
+ * stream for details.
+ *
+ * On failure the first error that happened will be reported, but the close
+ * operation will finish as much as possible. A stream that failed to
+ * close will still return %G_IO_ERROR_CLOSED all operations. Still, it
+ * is important to check and report the error to the user, otherwise
+ * there might be a loss of data as all data might not be written.
+ * 
+ * If @cancellable is not NULL, then the operation can be cancelled by
+ * triggering the cancellable object from another thread. If the operation
+ * was cancelled, the error %G_IO_ERROR_CANCELLED will be returned.
+ * Cancelling a close will still leave the stream closed, but there some streams
+ * can use a faster close that doesn't block to e.g. check errors. On
+ * cancellation (as with any error) there is no guarantee that all written
+ * data will reach the target. 
+ *
+ * Return value: %TRUE on success, %FALSE on failure
+ **/
+gboolean
+g_output_stream_close (GOutputStream  *stream,
+                      GCancellable   *cancellable,
+                      GError        **error)
+{
+  GOutputStreamClass *class;
+  gboolean res;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+
+  if (stream->priv->closed)
+    return TRUE;
+
+  if (stream->priv->pending)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_PENDING,
+                  _("Stream has outstanding operation"));
+      return FALSE;
+    }
+
+  res = g_output_stream_flush (stream, cancellable, error);
+
+  stream->priv->pending = TRUE;
+  
+  if (cancellable)
+    g_push_current_cancellable (cancellable);
+
+  if (!res)
+    {
+      /* flushing caused the error that we want to return,
+       * but we still want to close the underlying stream if possible
+       */
+      if (class->close)
+       class->close (stream, cancellable, NULL);
+    }
+  else
+    {
+      res = TRUE;
+      if (class->close)
+       res = class->close (stream, cancellable, error);
+    }
+  
+  if (cancellable)
+    g_pop_current_cancellable (cancellable);
+  
+  stream->priv->closed = TRUE;
+  stream->priv->pending = FALSE;
+  
+  return res;
+}
+
+static void
+async_ready_callback_wrapper (GObject *source_object,
+                             GAsyncResult *res,
+                             gpointer      user_data)
+{
+  GOutputStream *stream = G_OUTPUT_STREAM (source_object);
+
+  stream->priv->pending = FALSE;
+  if (stream->priv->outstanding_callback)
+    (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+static void
+async_ready_close_callback_wrapper (GObject *source_object,
+                                   GAsyncResult *res,
+                                   gpointer user_data)
+{
+  GOutputStream *stream = G_OUTPUT_STREAM (source_object);
+
+  stream->priv->pending = FALSE;
+  stream->priv->closed = TRUE;
+  if (stream->priv->outstanding_callback)
+    (*stream->priv->outstanding_callback) (source_object, res, user_data);
+  g_object_unref (stream);
+}
+
+/**
+ * g_output_stream_write_async:
+ * @stream: A #GOutputStream.
+ * @buffer: the buffer containing the data to write. 
+ * @count: the number of bytes to write
+ * @io_priority: the io priority of the request. the io priority of the request
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ *
+ * Request an asynchronous write of @count bytes from @buffer into the stream.
+ * When the operation is finished @callback will be called, giving the results.
+ *
+ * During an async request no other sync and async calls are allowed, and will
+ * result in %G_IO_ERROR_PENDING errors. 
+ *
+ * A value of @count larger than %G_MAXSSIZE will cause a %G_IO_ERROR_INVALID_ARGUMENT error.
+ *
+ * On success, the number of bytes written will be passed to the
+ * @callback. It is not an error if this is not the same as the requested size, as it
+ * can happen e.g. on a partial i/o error, but generally we try to write
+ * as many bytes as requested. 
+ *
+ * Any outstanding i/o request with higher priority (lower numerical value) will
+ * be executed before an outstanding request with lower priority. Default
+ * priority is %G_PRIORITY_DEFAULT.
+ *
+ * The asyncronous methods have a default fallback that uses threads to implement
+ * asynchronicity, so they are optional for inheriting classes. However, if you
+ * override one you must override all.
+ *
+ * For the synchronous, blocking version of this function, see g_output_stream_write().
+ **/
+void
+g_output_stream_write_async (GOutputStream       *stream,
+                            const void          *buffer,
+                            gsize                count,
+                            int                  io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  GOutputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+  g_return_if_fail (buffer != NULL);
+
+  if (count == 0)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_output_stream_write_async);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+  if (((gssize) count) < 0)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT,
+                                          _("Too large count value passed to g_output_stream_write_async"));
+      return;
+    }
+
+  if (stream->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+
+  stream->priv->pending = TRUE;
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->write_async (stream, buffer, count, io_priority, cancellable,
+                     async_ready_callback_wrapper, user_data);
+}
+
+/**
+ * g_output_stream_write_finish:
+ * @stream: a #GOutputStream.
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * 
+ * 
+ * Returns: 
+ **/
+gssize
+g_output_stream_write_finish (GOutputStream *stream,
+                             GAsyncResult *result,
+                             GError **error)
+{
+  GSimpleAsyncResult *simple;
+  GOutputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), -1);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return -1;
+
+      /* Special case writes of 0 bytes */
+      if (g_simple_async_result_get_source_tag (simple) == g_output_stream_write_async)
+       return 0;
+    }
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+  return class->write_finish (stream, result, error);
+}
+
+typedef struct {
+  GInputStream *source;
+  gpointer user_data;
+  GAsyncReadyCallback callback;
+} SpliceUserData;
+
+static void
+async_ready_splice_callback_wrapper (GObject *source_object,
+                                    GAsyncResult *res,
+                                    gpointer _data)
+{
+  GOutputStream *stream = G_OUTPUT_STREAM (source_object);
+  SpliceUserData *data = _data;
+  
+  stream->priv->pending = FALSE;
+  
+  if (data->callback)
+    (*data->callback) (source_object, res, data->user_data);
+  
+  g_object_unref (stream);
+  g_object_unref (data->source);
+  g_free (data);
+}
+
+/**
+ * g_output_stream_splice_async:
+ * @stream:
+ * @source:
+ * @flags:
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @callback:
+ * @user_data:
+ * 
+ **/
+void
+g_output_stream_splice_async (GOutputStream             *stream,
+                             GInputStream              *source,
+                             GOutputStreamSpliceFlags   flags,
+                             int                        io_priority,
+                             GCancellable              *cancellable,
+                             GAsyncReadyCallback        callback,
+                             gpointer                   user_data)
+{
+  GOutputStreamClass *class;
+  SpliceUserData *data;
+
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+  g_return_if_fail (G_IS_INPUT_STREAM (source));
+
+  if (stream->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Target stream is already closed"));
+      return;
+    }
+
+  if (g_input_stream_is_closed (source))
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Source stream is already closed"));
+      return;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+
+  stream->priv->pending = TRUE;
+
+  data = g_new0 (SpliceUserData, 1);
+  data->callback = callback;
+  data->user_data = user_data;
+  data->source = g_object_ref (source);
+  
+  g_object_ref (stream);
+  class->splice_async (stream, source, flags, io_priority, cancellable,
+                     async_ready_splice_callback_wrapper, data);
+}
+
+/**
+ * g_output_stream_splice_finish:
+ * @stream: a #GOutputStream.
+ * @result: a #GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ *
+ * Returns: 
+ **/
+gssize
+g_output_stream_splice_finish (GOutputStream             *stream,
+                              GAsyncResult              *result,
+                              GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  GOutputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), -1);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), -1);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return -1;
+    }
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+  return class->splice_finish (stream, result, error);
+}
+
+/**
+ * g_output_stream_flush_async:
+ * @stream:
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore.
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data:
+ * 
+ **/
+void
+g_output_stream_flush_async (GOutputStream       *stream,
+                            int                  io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
+{
+  GOutputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+
+  if (stream->priv->closed)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_CLOSED,
+                                          _("Stream is already closed"));
+      return;
+    }
+  
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+  
+  if (class->flush_async == NULL)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_output_stream_flush_async);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+      
+  stream->priv->pending = TRUE;
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->flush_async (stream, io_priority, cancellable,
+                     async_ready_callback_wrapper, user_data);
+}
+
+/**
+ * g_output_stream_flush_finish:
+ * @stream: a #GOutputStream.
+ * @result: a GAsyncResult.
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: %TRUE if flush operation suceeded, %FALSE otherwise.
+ **/
+gboolean
+g_output_stream_flush_finish (GOutputStream *stream,
+                             GAsyncResult *result,
+                             GError **error)
+{
+  GSimpleAsyncResult *simple;
+  GOutputStreamClass *klass;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+
+      /* Special case default implementation */
+      if (g_simple_async_result_get_source_tag (simple) == g_output_stream_flush_async)
+       return TRUE;
+    }
+
+  klass = G_OUTPUT_STREAM_GET_CLASS (stream);
+  return klass->flush_finish (stream, result, error);
+}
+
+
+/**
+ * g_output_stream_close_async:
+ * @stream: A #GOutputStream.
+ * @callback: callback to call when the request is satisfied
+ * @user_data: the data to pass to callback function
+ * @cancellable: optional cancellable object
+ *
+ * Requests an asynchronous closes of the stream, releasing resources related to it.
+ * When the operation is finished @callback will be called, giving the results.
+ *
+ * For behaviour details see g_output_stream_close().
+ *
+ * The asyncronous methods have a default fallback that uses threads to implement
+ * asynchronicity, so they are optional for inheriting classes. However, if you
+ * override one you must override all.
+ **/
+void
+g_output_stream_close_async (GOutputStream      *stream,
+                            int                 io_priority,
+                            GCancellable       *cancellable,
+                            GAsyncReadyCallback callback,
+                            gpointer            user_data)
+{
+  GOutputStreamClass *class;
+  GSimpleAsyncResult *simple;
+
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+  
+  if (stream->priv->closed)
+    {
+      simple = g_simple_async_result_new (G_OBJECT (stream),
+                                         callback,
+                                         user_data,
+                                         g_output_stream_close_async);
+      g_simple_async_result_complete_in_idle (simple);
+      g_object_unref (simple);
+      return;
+    }
+
+  if (stream->priv->pending)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (stream),
+                                          callback,
+                                          user_data,
+                                          G_IO_ERROR, G_IO_ERROR_PENDING,
+                                          _("Stream has outstanding operation"));
+      return;
+    }
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+  stream->priv->pending = TRUE;
+  stream->priv->outstanding_callback = callback;
+  g_object_ref (stream);
+  class->close_async (stream, io_priority, cancellable,
+                     async_ready_close_callback_wrapper, user_data);
+}
+
+/**
+ * g_output_stream_close_finish:
+ * @stream:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: %TRUE, %FALSE otherwise.
+ **/
+gboolean
+g_output_stream_close_finish (GOutputStream *stream,
+                             GAsyncResult *result,
+                             GError **error)
+{
+  GSimpleAsyncResult *simple;
+  GOutputStreamClass *class;
+
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+
+      /* Special case already closed */
+      if (g_simple_async_result_get_source_tag (simple) == g_output_stream_close_async)
+       return TRUE;
+    }
+
+  class = G_OUTPUT_STREAM_GET_CLASS (stream);
+  return class->close_finish (stream, result, error);
+}
+
+/**
+ * g_output_stream_is_closed:
+ * @stream:
+ * 
+ * Returns: %TRUE if @stream is closed. %FALSE otherwise. 
+ **/
+gboolean
+g_output_stream_is_closed (GOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), TRUE);
+  
+  return stream->priv->closed;
+}
+
+/**
+ * g_output_stream_has_pending:
+ * @stream:
+ * 
+ * Returns: %TRUE if @stream has pending actions. 
+ **/
+gboolean
+g_output_stream_has_pending (GOutputStream *stream)
+{
+  g_return_val_if_fail (G_IS_OUTPUT_STREAM (stream), FALSE);
+  
+  return stream->priv->pending;
+}
+
+/**
+ * g_output_stream_set_pending:
+ * @stream:
+ * @pending: 
+ * 
+ * Sets the @stream as having pending actions. 
+ * 
+ **/
+void
+g_output_stream_set_pending (GOutputStream              *stream,
+                           gboolean                   pending)
+{
+  g_return_if_fail (G_IS_OUTPUT_STREAM (stream));
+  
+  stream->priv->pending = pending;
+}
+
+
+/********************************************
+ *   Default implementation of async ops    *
+ ********************************************/
+
+typedef struct {
+  const void         *buffer;
+  gsize               count_requested;
+  gssize              count_written;
+} WriteData;
+
+static void
+write_async_thread (GSimpleAsyncResult *res,
+                  GObject *object,
+                  GCancellable *cancellable)
+{
+  WriteData *op;
+  GOutputStreamClass *class;
+  GError *error = NULL;
+
+  class = G_OUTPUT_STREAM_GET_CLASS (object);
+  op = g_simple_async_result_get_op_res_gpointer (res);
+  op->count_written = class->write (G_OUTPUT_STREAM (object), op->buffer, op->count_requested,
+                                   cancellable, &error);
+  if (op->count_written == -1)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+static void
+g_output_stream_real_write_async (GOutputStream       *stream,
+                                 const void          *buffer,
+                                 gsize                count,
+                                 int                  io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GSimpleAsyncResult *res;
+  WriteData *op;
+
+  op = g_new0 (WriteData, 1);
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_output_stream_real_write_async);
+  g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+  op->buffer = buffer;
+  op->count_requested = count;
+  
+  g_simple_async_result_run_in_thread (res, write_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static gssize
+g_output_stream_real_write_finish (GOutputStream *stream,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  WriteData *op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_output_stream_real_write_async);
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  return op->count_written;
+}
+
+typedef struct {
+  GInputStream *source;
+  GOutputStreamSpliceFlags flags;
+  gssize bytes_copied;
+} SpliceData;
+
+static void
+splice_async_thread (GSimpleAsyncResult *result,
+                    GObject *object,
+                    GCancellable *cancellable)
+{
+  SpliceData *op;
+  GOutputStreamClass *class;
+  GError *error = NULL;
+  GOutputStream *stream;
+
+  stream = G_OUTPUT_STREAM (object);
+  class = G_OUTPUT_STREAM_GET_CLASS (object);
+  op = g_simple_async_result_get_op_res_gpointer (result);
+  
+  stream->priv->pending = FALSE;
+  op->bytes_copied =
+    g_output_stream_splice (stream,
+                           op->source,
+                           op->flags,
+                           cancellable,
+                           &error);
+  stream->priv->pending = TRUE;
+
+  if (op->bytes_copied == -1)
+    {
+      g_simple_async_result_set_from_error (result, error);
+      g_error_free (error);
+    }
+}
+
+static void
+g_output_stream_real_splice_async  (GOutputStream             *stream,
+                                   GInputStream              *source,
+                                   GOutputStreamSpliceFlags   flags,
+                                   int                        io_priority,
+                                   GCancellable              *cancellable,
+                                   GAsyncReadyCallback        callback,
+                                   gpointer                   user_data)
+{
+  GSimpleAsyncResult *res;
+  SpliceData *op;
+
+  op = g_new0 (SpliceData, 1);
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_output_stream_real_splice_async);
+  g_simple_async_result_set_op_res_gpointer (res, op, g_free);
+  op->flags = flags;
+  op->source = source;
+
+  /* TODO: In the case where both source and destintion have
+     non-threadbased async calls we can use a true async copy here */
+  
+  g_simple_async_result_run_in_thread (res, splice_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static gssize
+g_output_stream_real_splice_finish (GOutputStream             *stream,
+                                   GAsyncResult              *result,
+                                   GError                   **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  SpliceData *op;
+
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_output_stream_real_splice_async);
+  op = g_simple_async_result_get_op_res_gpointer (simple);
+  return op->bytes_copied;
+}
+
+
+
+static void
+flush_async_thread (GSimpleAsyncResult *res,
+                   GObject *object,
+                   GCancellable *cancellable)
+{
+  GOutputStreamClass *class;
+  gboolean result;
+  GError *error = NULL;
+
+  class = G_OUTPUT_STREAM_GET_CLASS (object);
+  result = TRUE;
+  if (class->flush)
+    result = class->flush (G_OUTPUT_STREAM (object), cancellable, &error);
+
+  if (!result)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+static void
+g_output_stream_real_flush_async (GOutputStream       *stream,
+                                 int                  io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GSimpleAsyncResult *res;
+
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_output_stream_real_write_async);
+  
+  g_simple_async_result_run_in_thread (res, flush_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_output_stream_real_flush_finish (GOutputStream *stream,
+                                  GAsyncResult *result,
+                                  GError **error)
+{
+  return TRUE;
+}
+
+static void
+close_async_thread (GSimpleAsyncResult *res,
+                   GObject *object,
+                   GCancellable *cancellable)
+{
+  GOutputStreamClass *class;
+  GError *error = NULL;
+  gboolean result;
+
+  /* Auto handling of cancelation disabled, and ignore
+     cancellation, since we want to close things anyway, although
+     possibly in a quick-n-dirty way. At least we never want to leak
+     open handles */
+  
+  class = G_OUTPUT_STREAM_GET_CLASS (object);
+  result = class->close (G_OUTPUT_STREAM (object), cancellable, &error);
+  if (!result)
+    {
+      g_simple_async_result_set_from_error (res, error);
+      g_error_free (error);
+    }
+}
+
+static void
+g_output_stream_real_close_async (GOutputStream      *stream,
+                                 int                 io_priority,
+                                 GCancellable       *cancellable,
+                                 GAsyncReadyCallback callback,
+                                 gpointer            user_data)
+{
+  GSimpleAsyncResult *res;
+  
+  res = g_simple_async_result_new (G_OBJECT (stream), callback, user_data, g_output_stream_real_close_async);
+
+  g_simple_async_result_set_handle_cancellation (res, FALSE);
+  
+  g_simple_async_result_run_in_thread (res, close_async_thread, io_priority, cancellable);
+  g_object_unref (res);
+}
+
+static gboolean
+g_output_stream_real_close_finish (GOutputStream              *stream,
+                                  GAsyncResult              *result,
+                                  GError                   **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_output_stream_real_close_async);
+  return TRUE;
+}
diff --git a/gio/goutputstream.h b/gio/goutputstream.h
new file mode 100644 (file)
index 0000000..4bfb621
--- /dev/null
@@ -0,0 +1,202 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_OUTPUT_STREAM_H__
+#define __G_OUTPUT_STREAM_H__
+
+#include <glib-object.h>
+#include <gio/gioerror.h>
+#include <gio/gasyncresult.h>
+#include <gio/gcancellable.h>
+#include <gio/ginputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_OUTPUT_STREAM         (g_output_stream_get_type ())
+#define G_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_OUTPUT_STREAM, GOutputStream))
+#define G_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_OUTPUT_STREAM, GOutputStreamClass))
+#define G_IS_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_OUTPUT_STREAM))
+#define G_IS_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_OUTPUT_STREAM))
+#define G_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_OUTPUT_STREAM, GOutputStreamClass))
+
+typedef enum {
+  G_OUTPUT_STREAM_SPLICE_FLAGS_NONE = 0,
+  G_OUTPUT_STREAM_SPLICE_FLAGS_CLOSE_SOURCE = 1 << 0,
+  G_OUTPUT_STREAM_SPLICE_FLAGS_CLOSE_TARGET = 1 << 1
+} GOutputStreamSpliceFlags;
+
+typedef struct _GOutputStream         GOutputStream;
+typedef struct _GOutputStreamClass    GOutputStreamClass;
+typedef struct _GOutputStreamPrivate  GOutputStreamPrivate;
+
+struct _GOutputStream
+{
+  GObject parent;
+  
+  /*< private >*/
+  GOutputStreamPrivate *priv;
+};
+
+
+struct _GOutputStreamClass
+{
+  GObjectClass parent_class;
+
+  /* Sync ops: */
+  
+  gssize      (* write)  (GOutputStream *stream,
+                         const void *buffer,
+                         gsize count,
+                         GCancellable *cancellable,
+                         GError **error);
+  gssize      (* splice) (GOutputStream *stream,
+                         GInputStream  *source,
+                         GOutputStreamSpliceFlags   flags,
+                         GCancellable  *cancellable,
+                         GError       **error);
+  gboolean    (* flush)         (GOutputStream *stream,
+                         GCancellable  *cancellable,
+                         GError       **error);
+  gboolean    (* close)         (GOutputStream *stream,
+                         GCancellable  *cancellable,
+                         GError       **error);
+  
+  /* Async ops: (optional in derived classes) */
+
+  void     (* write_async)  (GOutputStream       *stream,
+                            const void          *buffer,
+                            gsize                count,
+                            int                  io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data);
+  gssize   (* write_finish) (GOutputStream       *stream,
+                            GAsyncResult        *result,
+                            GError             **error);
+  void     (* splice_async) (GOutputStream       *stream,
+                            GInputStream        *source,
+                            GOutputStreamSpliceFlags flags,
+                            int                  io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             data);
+  gssize   (* splice_finish)(GOutputStream       *stream,
+                            GAsyncResult        *result,
+                            GError             **error);
+  void     (* flush_async)  (GOutputStream       *stream,
+                            int                  io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data);
+  gboolean (* flush_finish) (GOutputStream       *stream,
+                            GAsyncResult        *result,
+                            GError             **error);
+  void     (* close_async)  (GOutputStream       *stream,
+                            int                  io_priority,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data);
+  gboolean (* close_finish) (GOutputStream       *stream,
+                            GAsyncResult        *result,
+                            GError             **error);
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+};
+
+GType g_output_stream_get_type (void) G_GNUC_CONST;
+  
+gssize   g_output_stream_write         (GOutputStream             *stream,
+                                       const void                *buffer,
+                                       gsize                      count,
+                                       GCancellable              *cancellable,
+                                       GError                   **error);
+gboolean g_output_stream_write_all     (GOutputStream             *stream,
+                                       const void                *buffer,
+                                       gsize                      count,
+                                       gsize                     *bytes_written,
+                                       GCancellable              *cancellable,
+                                       GError                   **error);
+gssize   g_output_stream_splice        (GOutputStream             *stream,
+                                       GInputStream              *source,
+                                       GOutputStreamSpliceFlags   flags,
+                                       GCancellable              *cancellable,
+                                       GError                   **error);
+gboolean g_output_stream_flush         (GOutputStream             *stream,
+                                       GCancellable              *cancellable,
+                                       GError                   **error);
+gboolean g_output_stream_close         (GOutputStream             *stream,
+                                       GCancellable              *cancellable,
+                                       GError                   **error);
+void     g_output_stream_write_async   (GOutputStream             *stream,
+                                       const void                *buffer,
+                                       gsize                      count,
+                                       int                        io_priority,
+                                       GCancellable              *cancellable,
+                                       GAsyncReadyCallback        callback,
+                                       gpointer                   user_data);
+gssize   g_output_stream_write_finish  (GOutputStream             *stream,
+                                       GAsyncResult              *result,
+                                       GError                   **error);
+void     g_output_stream_splice_async  (GOutputStream             *stream,
+                                       GInputStream              *source,
+                                       GOutputStreamSpliceFlags   flags,
+                                       int                        io_priority,
+                                       GCancellable              *cancellable,
+                                       GAsyncReadyCallback        callback,
+                                       gpointer                   user_data);
+gssize   g_output_stream_splice_finish (GOutputStream             *stream,
+                                       GAsyncResult              *result,
+                                       GError                   **error);
+void     g_output_stream_flush_async   (GOutputStream             *stream,
+                                       int                        io_priority,
+                                       GCancellable              *cancellable,
+                                       GAsyncReadyCallback        callback,
+                                       gpointer                   user_data);
+gboolean g_output_stream_flush_finish  (GOutputStream             *stream,
+                                       GAsyncResult              *result,
+                                       GError                   **error);
+void     g_output_stream_close_async   (GOutputStream             *stream,
+                                       int                        io_priority,
+                                       GCancellable              *cancellable,
+                                       GAsyncReadyCallback        callback,
+                                       gpointer                   user_data);
+gboolean g_output_stream_close_finish  (GOutputStream             *stream,
+                                       GAsyncResult              *result,
+                                       GError                   **error);
+
+gboolean g_output_stream_is_closed     (GOutputStream             *stream);
+gboolean g_output_stream_has_pending   (GOutputStream             *stream);
+void     g_output_stream_set_pending   (GOutputStream             *stream,
+                                       gboolean                   pending);
+
+
+G_END_DECLS
+
+#endif /* __G_OUTPUT_STREAM_H__ */
diff --git a/gio/gpollfilemonitor.c b/gio/gpollfilemonitor.c
new file mode 100644 (file)
index 0000000..0b5a5e7
--- /dev/null
@@ -0,0 +1,226 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <string.h>
+
+#include "gpollfilemonitor.h"
+#include "gfilemonitor.h"
+
+static gboolean g_poll_file_monitor_cancel (GFileMonitor* monitor);
+static void schedule_poll_timeout (GPollFileMonitor* poll_monitor);
+
+struct _GPollFileMonitor
+{
+  GFileMonitor parent_instance;
+  GFile *file;
+  GFileInfo *last_info;
+  guint timeout;
+};
+
+#define POLL_TIME_SECS 5
+
+G_DEFINE_TYPE (GPollFileMonitor, g_poll_file_monitor, G_TYPE_FILE_MONITOR)
+
+static void
+g_poll_file_monitor_finalize (GObject* object)
+{
+  GPollFileMonitor* poll_monitor;
+  
+  poll_monitor = G_POLL_FILE_MONITOR (object);
+
+  g_object_unref (poll_monitor->file);
+
+  if (G_OBJECT_CLASS (g_poll_file_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_poll_file_monitor_parent_class)->finalize) (object);
+}
+
+
+static void
+g_poll_file_monitor_class_init (GPollFileMonitorClass* klass)
+{
+  GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+  GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_poll_file_monitor_finalize;
+
+  file_monitor_class->cancel = g_poll_file_monitor_cancel;
+}
+
+static void
+g_poll_file_monitor_init (GPollFileMonitor* poll_monitor)
+{
+}
+
+static int 
+safe_strcmp (const char *a, const char *b)
+{
+  if (a == NULL && b == NULL)
+    return 0;
+  if (a == NULL)
+    return -1;
+  if (b == NULL)
+    return 1;
+  
+  return strcmp (a, b);
+}
+
+static int
+calc_event_type (GFileInfo *last,
+                GFileInfo *new)
+{
+  if (last == NULL && new == NULL)
+    return -1;
+
+  if (last == NULL && new != NULL)
+    return G_FILE_MONITOR_EVENT_CREATED;
+  
+  if (last != NULL && new == NULL)
+    return G_FILE_MONITOR_EVENT_DELETED;
+
+  if (safe_strcmp (g_file_info_get_etag (last),
+                  g_file_info_get_etag (new)))
+    return G_FILE_MONITOR_EVENT_CHANGED;
+  
+  if (g_file_info_get_size (last) !=
+      g_file_info_get_size (new))
+    return G_FILE_MONITOR_EVENT_CHANGED;
+
+  return -1;
+}
+
+static void
+got_new_info (GObject *source_object,
+             GAsyncResult *res,
+             gpointer user_data)
+{
+  GPollFileMonitor* poll_monitor = user_data;
+  GFileInfo *info;
+  int event;
+
+  info = g_file_query_info_finish (poll_monitor->file, res, NULL);
+
+  if (!g_file_monitor_is_cancelled (G_FILE_MONITOR (poll_monitor)))
+    {
+      event = calc_event_type (poll_monitor->last_info, info);
+
+      if (event != -1)
+       {
+         g_file_monitor_emit_event (G_FILE_MONITOR (poll_monitor),
+                                    poll_monitor->file,
+                                    NULL, event);
+         /* We're polling so slowly anyway, so always emit the done hint */
+         if (event == G_FILE_MONITOR_EVENT_CHANGED)
+           g_file_monitor_emit_event (G_FILE_MONITOR (poll_monitor),
+                                      poll_monitor->file,
+                                      NULL, G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT);
+       }
+      
+      if (poll_monitor->last_info)
+       {
+         g_object_unref (poll_monitor->last_info);
+         poll_monitor->last_info = NULL;
+       }
+      
+      if (info)
+       poll_monitor->last_info = g_object_ref (info);
+      
+      schedule_poll_timeout (poll_monitor);
+    }
+
+  if (info)
+    g_object_unref (info);
+  
+  g_object_unref (poll_monitor);
+}
+
+static gboolean
+poll_file_timeout (gpointer data)
+{
+  GPollFileMonitor* poll_monitor = data;
+
+  poll_monitor->timeout = FALSE;
+
+  g_file_query_info_async (poll_monitor->file, G_FILE_ATTRIBUTE_ETAG_VALUE "," G_FILE_ATTRIBUTE_STD_SIZE,
+                        0, 0, NULL, got_new_info, g_object_ref (poll_monitor));
+  
+  return FALSE;
+}
+
+static void
+schedule_poll_timeout (GPollFileMonitor* poll_monitor)
+{
+  poll_monitor->timeout = g_timeout_add_seconds (POLL_TIME_SECS, poll_file_timeout, poll_monitor);
+ }
+
+static void
+got_initial_info (GObject *source_object,
+                 GAsyncResult *res,
+                 gpointer user_data)
+{
+  GPollFileMonitor* poll_monitor = user_data;
+  GFileInfo *info;
+
+  info = g_file_query_info_finish (poll_monitor->file, res, NULL);
+
+  poll_monitor->last_info = info;
+
+  if (!g_file_monitor_is_cancelled (G_FILE_MONITOR (poll_monitor)))
+    schedule_poll_timeout (poll_monitor);
+  
+  g_object_unref (poll_monitor);
+}
+
+/**
+ * g_poll_file_monitor_new:
+ * @file:
+ * 
+ * Returns a new #GFileMonitor for the given #GFile. 
+ **/
+GFileMonitor*
+g_poll_file_monitor_new (GFile *file)
+{
+  GPollFileMonitor* poll_monitor;
+  
+  poll_monitor = g_object_new (G_TYPE_POLL_FILE_MONITOR, NULL);
+
+  poll_monitor->file = g_object_ref (file);
+
+  g_file_query_info_async (file, G_FILE_ATTRIBUTE_ETAG_VALUE "," G_FILE_ATTRIBUTE_STD_SIZE,
+                          0, 0, NULL, got_initial_info, g_object_ref (poll_monitor));
+  
+  return G_FILE_MONITOR (poll_monitor);
+}
+
+static gboolean
+g_poll_file_monitor_cancel (GFileMonitor* monitor)
+{
+  GPollFileMonitor *poll_monitor = G_POLL_FILE_MONITOR (monitor);
+  
+  if (poll_monitor->timeout)
+    {
+      g_source_remove (poll_monitor->timeout);
+      poll_monitor->timeout = 0;
+    }
+  
+  return TRUE;
+}
diff --git a/gio/gpollfilemonitor.h b/gio/gpollfilemonitor.h
new file mode 100644 (file)
index 0000000..2d4e2a3
--- /dev/null
@@ -0,0 +1,50 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_POLL_FILE_MONITOR_H__
+#define __G_POLL_FILE_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gfilemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_POLL_FILE_MONITOR               (g_poll_file_monitor_get_type ())
+#define G_POLL_FILE_MONITOR(o)                 (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_POLL_FILE_MONITOR, GPollFileMonitor))
+#define G_POLL_FILE_MONITOR_CLASS(k)           (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_POLL_FILE_MONITOR, GPollFileMonitorClass))
+#define G_IS_POLL_FILE_MONITOR(o)              (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_POLL_FILE_MONITOR))
+#define G_IS_POLL_FILE_MONITOR_CLASS(k)        (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_POLL_FILE_MONITOR))
+
+typedef struct _GPollFileMonitor      GPollFileMonitor;
+typedef struct _GPollFileMonitorClass GPollFileMonitorClass;
+
+struct _GPollFileMonitorClass {
+  GFileMonitorClass parent_class;
+};
+
+GType g_poll_file_monitor_get_type (void) G_GNUC_CONST;
+
+GFileMonitor* g_poll_file_monitor_new (GFile *file);
+
+G_END_DECLS
+
+#endif /* __G_POLL_FILE_MONITOR_H__ */
diff --git a/gio/gseekable.c b/gio/gseekable.c
new file mode 100644 (file)
index 0000000..90f53da
--- /dev/null
@@ -0,0 +1,168 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gseekable.h"
+#include "glibintl.h"
+
+static void g_seekable_base_init (gpointer g_class);
+
+
+GType
+g_seekable_get_type (void)
+{
+  static GType seekable_type = 0;
+
+  if (! seekable_type)
+    {
+      static const GTypeInfo seekable_info =
+      {
+        sizeof (GSeekableIface), /* class_size */
+       g_seekable_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       NULL,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      seekable_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GSeekable"),
+                               &seekable_info, 0);
+
+      g_type_interface_add_prerequisite (seekable_type, G_TYPE_OBJECT);
+    }
+
+  return seekable_type;
+}
+
+static void
+g_seekable_base_init (gpointer g_class)
+{
+}
+
+/**
+ * g_seekable_tell:
+ * @seekable:
+ * 
+ * Returns: a goffset.
+ **/
+goffset
+g_seekable_tell (GSeekable *seekable)
+{
+  GSeekableIface *iface;
+
+  g_return_val_if_fail (G_IS_SEEKABLE (seekable), 0);
+
+  iface = G_SEEKABLE_GET_IFACE (seekable);
+
+  return (* iface->tell) (seekable);
+}
+
+/**
+ * g_seekable_can_seek:
+ * @seekable:
+ * 
+ * Returns: %TRUE if @seekable can be seeked. %FALSE otherwise.
+ **/
+gboolean
+g_seekable_can_seek (GSeekable *seekable)
+{
+  GSeekableIface *iface;
+  
+  g_return_val_if_fail (G_IS_SEEKABLE (seekable), FALSE);
+
+  iface = G_SEEKABLE_GET_IFACE (seekable);
+
+  return (* iface->can_seek) (seekable);
+}
+
+/**
+ * g_seekable_seek:
+ * @seekable:
+ * @offset:
+ * @type:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: %TRUE, %FALSE otherwise.
+ **/
+gboolean
+g_seekable_seek (GSeekable     *seekable,
+                goffset        offset,
+                GSeekType      type,
+                GCancellable  *cancellable,
+                GError       **error)
+{
+  GSeekableIface *iface;
+  
+  g_return_val_if_fail (G_IS_SEEKABLE (seekable), FALSE);
+
+  iface = G_SEEKABLE_GET_IFACE (seekable);
+
+  return (* iface->seek) (seekable, offset, type, cancellable, error);
+}
+
+/**
+ * g_seekable_can_truncate:
+ * @seekable:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_seekable_can_truncate (GSeekable *seekable)
+{
+  GSeekableIface *iface;
+  
+  g_return_val_if_fail (G_IS_SEEKABLE (seekable), FALSE);
+
+  iface = G_SEEKABLE_GET_IFACE (seekable);
+
+  return (* iface->can_truncate) (seekable);
+}
+
+/**
+ * g_seekable_truncate:
+ * @seekable:
+ * @offset:
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ **/
+gboolean
+g_seekable_truncate (GSeekable     *seekable,
+                    goffset        offset,
+                    GCancellable  *cancellable,
+                    GError       **error)
+{
+  GSeekableIface *iface;
+  
+  g_return_val_if_fail (G_IS_SEEKABLE (seekable), FALSE);
+
+  iface = G_SEEKABLE_GET_IFACE (seekable);
+
+  return (* iface->truncate) (seekable, offset, cancellable, error);
+}
+
diff --git a/gio/gseekable.h b/gio/gseekable.h
new file mode 100644 (file)
index 0000000..33fc619
--- /dev/null
@@ -0,0 +1,81 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_SEEKABLE_H__
+#define __G_SEEKABLE_H__
+
+#include <glib-object.h>
+#include <gio/gcancellable.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SEEKABLE            (g_seekable_get_type ())
+#define G_SEEKABLE(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_SEEKABLE, GSeekable))
+#define G_IS_SEEKABLE(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_SEEKABLE))
+#define G_SEEKABLE_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_SEEKABLE, GSeekableIface))
+
+typedef struct _GSeekable        GSeekable;
+typedef struct _GSeekableIface   GSeekableIface;
+
+struct _GSeekableIface
+{
+  GTypeInterface g_iface;
+
+  /* Virtual Table */
+  
+  goffset     (* tell)          (GSeekable    *seekable);
+  
+  gboolean    (* can_seek)       (GSeekable    *seekable);
+  gboolean    (* seek)          (GSeekable    *seekable,
+                                 goffset       offset,
+                                 GSeekType     type,
+                                 GCancellable *cancellable,
+                                 GError      **error);
+  
+  gboolean    (* can_truncate)   (GSeekable    *seekable);
+  gboolean    (* truncate)       (GSeekable    *seekable,
+                                 goffset       offset,
+                                 GCancellable *cancellable,
+                                 GError       **error);
+
+  /* TODO: Async seek/truncate */
+};
+
+GType g_seekable_get_type (void) G_GNUC_CONST;
+
+goffset  g_seekable_tell         (GSeekable     *seekable);
+gboolean g_seekable_can_seek     (GSeekable     *seekable);
+gboolean g_seekable_seek         (GSeekable     *seekable,
+                                 goffset        offset,
+                                 GSeekType      type,
+                                 GCancellable  *cancellable,
+                                 GError       **error);
+gboolean g_seekable_can_truncate (GSeekable     *seekable);
+gboolean g_seekable_truncate     (GSeekable     *seekable,
+                                 goffset        offset,
+                                 GCancellable  *cancellable,
+                                 GError       **error);
+
+G_END_DECLS
+
+
+#endif /* __G_SEEKABLE_H__ */
diff --git a/gio/gsimpleasyncresult.c b/gio/gsimpleasyncresult.c
new file mode 100644 (file)
index 0000000..3c69f19
--- /dev/null
@@ -0,0 +1,586 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "gsimpleasyncresult.h"
+#include "gioscheduler.h"
+#include <gio/gioerror.h>
+#include "glibintl.h"
+
+static void g_simple_async_result_async_result_iface_init (GAsyncResultIface       *iface);
+
+struct _GSimpleAsyncResult
+{
+  GObject parent_instance;
+
+  GObject *source_object;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GError *error;
+  gboolean failed;
+  gboolean handle_cancellation;
+
+  gpointer source_tag;
+
+  union {
+    gpointer v_pointer;
+    gboolean v_boolean;
+    gssize   v_ssize;
+  } op_res;
+
+  GDestroyNotify destroy_op_res;
+};
+
+struct _GSimpleAsyncResultClass
+{
+  GObjectClass parent_class;
+};
+
+
+G_DEFINE_TYPE_WITH_CODE (GSimpleAsyncResult, g_simple_async_result, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_RESULT,
+                                               g_simple_async_result_async_result_iface_init))
+
+static void
+g_simple_async_result_finalize (GObject *object)
+{
+  GSimpleAsyncResult *simple;
+
+  simple = G_SIMPLE_ASYNC_RESULT (object);
+
+  if (simple->source_object)
+    g_object_unref (simple->source_object);
+
+  if (simple->destroy_op_res)
+    simple->destroy_op_res (simple->op_res.v_pointer);
+
+  if (simple->error)
+    g_error_free (simple->error);
+  
+  if (G_OBJECT_CLASS (g_simple_async_result_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_simple_async_result_parent_class)->finalize) (object);
+}
+
+static void
+g_simple_async_result_class_init (GSimpleAsyncResultClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_simple_async_result_finalize;
+}
+
+static void
+g_simple_async_result_init (GSimpleAsyncResult *simple)
+{
+  simple->handle_cancellation = TRUE;
+}
+
+/**
+ * g_simple_async_result_new:
+ * @source_object:
+ * @callback:
+ * @user_data:
+ * @source_tag:
+ * 
+ * Returns: #GSimpleAsyncResult
+ **/
+GSimpleAsyncResult *
+g_simple_async_result_new (GObject *source_object,
+                          GAsyncReadyCallback callback,
+                          gpointer user_data,
+                          gpointer source_tag)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+
+  simple = g_object_new (G_TYPE_SIMPLE_ASYNC_RESULT, NULL);
+  simple->callback = callback;
+  simple->source_object = g_object_ref (source_object);
+  simple->user_data = user_data;
+  simple->source_tag = source_tag;
+  
+  return simple;
+}
+
+/**
+ * g_simple_async_result_new_from_error:
+ * @source_object:
+ * @callback:
+ * @user_data:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: #GSimpleAsyncResult
+ **/
+GSimpleAsyncResult *
+g_simple_async_result_new_from_error (GObject *source_object,
+                                     GAsyncReadyCallback callback,
+                                     gpointer user_data,
+                                     GError *error)
+{
+  GSimpleAsyncResult *simple;
+
+  g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+
+  simple = g_simple_async_result_new (source_object,
+                                     callback,
+                                     user_data, NULL);
+  g_simple_async_result_set_from_error (simple, error);
+
+  return simple;
+}
+
+/**
+ * g_simple_async_result_new_error:
+ * @source_object:
+ * @callback:
+ * @user_data:
+ * @domain:
+ * @code:
+ * @format:
+ * @...
+ * 
+ * Returns: #GSimpleAsyncResult.
+ **/
+GSimpleAsyncResult *
+g_simple_async_result_new_error (GObject *source_object,
+                                GAsyncReadyCallback callback,
+                                gpointer user_data,
+                                GQuark         domain,
+                                gint           code,
+                                const char    *format,
+                                ...)
+{
+  GSimpleAsyncResult *simple;
+  va_list args;
+  
+  g_return_val_if_fail (G_IS_OBJECT (source_object), NULL);
+  g_return_val_if_fail (domain != 0, NULL);
+  g_return_val_if_fail (format != NULL, NULL);
+
+  simple = g_simple_async_result_new (source_object,
+                                     callback,
+                                     user_data, NULL);
+
+  va_start (args, format);
+  g_simple_async_result_set_error_va (simple, domain, code, format, args);
+  va_end (args);
+  
+  return simple;
+}
+
+
+static gpointer
+g_simple_async_result_get_user_data (GAsyncResult *res)
+{
+  return G_SIMPLE_ASYNC_RESULT (res)->user_data;
+}
+
+static GObject *
+g_simple_async_result_get_source_object (GAsyncResult *res)
+{
+  if (G_SIMPLE_ASYNC_RESULT (res)->source_object)
+    return g_object_ref (G_SIMPLE_ASYNC_RESULT (res)->source_object);
+  return NULL;
+}
+
+static void
+g_simple_async_result_async_result_iface_init (GAsyncResultIface *iface)
+{
+  iface->get_user_data = g_simple_async_result_get_user_data;
+  iface->get_source_object = g_simple_async_result_get_source_object;
+}
+
+/**
+ * g_simple_async_result_set_handle_cancellation:
+ * @simple:
+ * @handle_cancellation:
+ * 
+ **/
+void
+g_simple_async_result_set_handle_cancellation (GSimpleAsyncResult *simple,
+                                              gboolean handle_cancellation)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  simple->handle_cancellation = handle_cancellation;
+}
+
+/**
+ * g_simple_async_result_get_source_tag:
+ * @simple:
+ * 
+ * Returns: 
+ **/
+gpointer
+g_simple_async_result_get_source_tag (GSimpleAsyncResult *simple)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
+  return simple->source_tag;
+}
+
+/**
+ * g_simple_async_result_result_propagate_error:
+ * @simple:
+ * @dest:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_simple_async_result_propagate_error (GSimpleAsyncResult *simple,
+                                      GError **dest)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE);
+
+  if (simple->failed)
+    {
+      g_propagate_error (dest, simple->error);
+      simple->error = NULL;
+      return TRUE;
+    }
+  return FALSE;
+}
+
+/**
+ * g_simple_async_result_set_op_res_gpointer:
+ * @simple:
+ * @op_res:
+ * @destroy_op_res:
+ * 
+ **/
+void
+g_simple_async_result_set_op_res_gpointer (GSimpleAsyncResult      *simple,
+                                           gpointer                 op_res,
+                                           GDestroyNotify           destroy_op_res)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+
+  simple->op_res.v_pointer = op_res;
+  simple->destroy_op_res = destroy_op_res;
+}
+
+/**
+ * g_simple_async_result_get_op_res_gpointer:
+ * @simple:
+ * 
+ * Returns: gpointer.
+ **/
+gpointer
+g_simple_async_result_get_op_res_gpointer (GSimpleAsyncResult      *simple)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), NULL);
+  return simple->op_res.v_pointer;
+}
+
+/**
+ * g_simple_async_result_set_op_res_gssize:
+ * @simple:
+ * @op_res:
+ * 
+ **/
+void
+g_simple_async_result_set_op_res_gssize   (GSimpleAsyncResult      *simple,
+                                           gssize                   op_res)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  simple->op_res.v_ssize = op_res;
+}
+
+/**
+ * g_simple_async_result_get_op_res_gssize:
+ * @simple:
+ * 
+ * Returns:
+ **/
+gssize
+g_simple_async_result_get_op_res_gssize   (GSimpleAsyncResult      *simple)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), 0);
+  return simple->op_res.v_ssize;
+}
+
+/**
+ * g_simple_async_result_set_op_res_gboolean:
+ * @simple:
+ * @op_res:
+ *  
+ **/
+void
+g_simple_async_result_set_op_res_gboolean (GSimpleAsyncResult      *simple,
+                                           gboolean                 op_res)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  simple->op_res.v_boolean = !!op_res;
+}
+
+/**
+ * g_simple_async_result_get_op_res_gboolean:
+ * @simple:
+ * 
+ * Returns a #gboolean. 
+ **/
+gboolean
+g_simple_async_result_get_op_res_gboolean (GSimpleAsyncResult      *simple)
+{
+  g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE);
+  return simple->op_res.v_boolean;
+}
+
+/**
+ * g_simple_async_result_set_from_error:
+ * @simple:
+ * @error: #GError.
+ * 
+ * Sets the result from given @error.
+ * 
+ **/
+void
+g_simple_async_result_set_from_error (GSimpleAsyncResult *simple,
+                                     GError *error)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  g_return_if_fail (error != NULL);
+
+  simple->error = g_error_copy (error);
+  simple->failed = TRUE;
+}
+
+static GError* 
+_g_error_new_valist (GQuark         domain,
+                    gint           code,
+                    const char    *format,
+                    va_list        args)
+{
+  GError *error;
+  char *message;
+
+  message = g_strdup_vprintf (format, args);
+
+  error = g_error_new_literal (domain, code, message);
+  g_free (message);
+  
+  return error;
+}
+
+/**
+ * g_simple_async_result_set_error_va:
+ * @simple:
+ * @domain:
+ * @code:
+ * @format:
+ * @args: va_list of arguments. 
+ * 
+ * Sets error va_list, suitable for language bindings.
+ * 
+ **/
+void
+g_simple_async_result_set_error_va (GSimpleAsyncResult *simple,
+                                   GQuark         domain,
+                                   gint           code,
+                                   const char    *format,
+                                   va_list        args)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  g_return_if_fail (domain != 0);
+  g_return_if_fail (format != NULL);
+
+  simple->error = _g_error_new_valist (domain, code, format, args);
+  simple->failed = TRUE;
+}
+
+/**
+ * g_simple_async_result_set_error:
+ * @simple:
+ * @domain:
+ * @code:
+ * @format:
+ * @...
+ * 
+ **/
+void
+g_simple_async_result_set_error (GSimpleAsyncResult *simple,
+                                GQuark         domain,
+                                gint           code,
+                                const char    *format,
+                                ...)
+{
+  va_list args;
+
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  g_return_if_fail (domain != 0);
+  g_return_if_fail (format != NULL);
+
+  va_start (args, format);
+  g_simple_async_result_set_error_va (simple, domain, code, format, args);
+  va_end (args);
+}
+
+/**
+ * g_simple_async_result_complete:
+ * @simple:
+ * 
+ **/
+void
+g_simple_async_result_complete (GSimpleAsyncResult *simple)
+{
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+
+  if (simple->callback)
+    simple->callback (simple->source_object,
+                     G_ASYNC_RESULT (simple),
+                     simple->user_data);
+}
+
+static gboolean
+complete_in_idle_cb (gpointer data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (data);
+
+  g_simple_async_result_complete (simple);
+
+  return FALSE;
+}
+
+/**
+ * g_simple_async_result_complete_in_idle:
+ * @simple:
+ *  
+ **/
+void
+g_simple_async_result_complete_in_idle (GSimpleAsyncResult *simple)
+{
+  GSource *source;
+  guint id;
+  
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  
+  g_object_ref (simple);
+  
+  source = g_idle_source_new ();
+  g_source_set_priority (source, G_PRIORITY_DEFAULT);
+  g_source_set_callback (source, complete_in_idle_cb, simple, g_object_unref);
+
+  id = g_source_attach (source, NULL);
+  g_source_unref (source);
+}
+
+typedef struct {
+  GSimpleAsyncResult *simple;
+  GSimpleAsyncThreadFunc func;
+} RunInThreadData;
+
+static void
+run_in_thread (GIOJob *job,
+              GCancellable *c,
+              gpointer _data)
+{
+  RunInThreadData *data = _data;
+  GSimpleAsyncResult *simple = data->simple;
+
+  if (simple->handle_cancellation &&
+      g_cancellable_is_cancelled (c))
+    {
+       g_simple_async_result_set_error (simple,
+                                       G_IO_ERROR,
+                                       G_IO_ERROR_CANCELLED,
+                                       _("Operation was cancelled"));
+    }
+  else
+    {
+      data->func (simple,
+                 simple->source_object,
+                 c);
+    }
+
+  g_simple_async_result_complete_in_idle (data->simple);
+  g_object_unref (data->simple);
+  g_free (data);
+}
+
+/**
+ * g_simple_async_result_run_in_thread:
+ * @simple:
+ * @func:
+ * @io_priority: the io priority of the request.
+ * @cancellable: optional #GCancellable object, %NULL to ignore. 
+ **/
+void
+g_simple_async_result_run_in_thread (GSimpleAsyncResult *simple,
+                                    GSimpleAsyncThreadFunc func,
+                                    int io_priority, 
+                                    GCancellable *cancellable)
+{
+  RunInThreadData *data;
+  
+  g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));
+  g_return_if_fail (func != NULL);
+
+  data = g_new (RunInThreadData, 1);
+  data->func = func;
+  data->simple = g_object_ref (simple);
+  g_schedule_io_job (run_in_thread, data, NULL, io_priority, cancellable);
+}
+
+/**
+ * g_simple_async_report_error_in_idle:
+ * @object:
+ * @callback:
+ * @user_data:
+ * @domain:
+ * @code:
+ * @format:
+ * @...
+ * 
+ **/
+void
+g_simple_async_report_error_in_idle (GObject *object,
+                                    GAsyncReadyCallback callback,
+                                    gpointer user_data,
+                                    GQuark         domain,
+                                    gint           code,
+                                    const char    *format,
+                                    ...)
+{
+  GSimpleAsyncResult *simple;
+  va_list args;
+  
+  g_return_if_fail (G_IS_OBJECT (object));
+  g_return_if_fail (domain != 0);
+  g_return_if_fail (format != NULL);
+
+  simple = g_simple_async_result_new (object,
+                                     callback,
+                                     user_data, NULL);
+
+  va_start (args, format);
+  g_simple_async_result_set_error_va (simple, domain, code, format, args);
+  va_end (args);
+  g_simple_async_result_complete_in_idle (simple);
+  g_object_unref (simple);
+}
diff --git a/gio/gsimpleasyncresult.h b/gio/gsimpleasyncresult.h
new file mode 100644 (file)
index 0000000..4c4ecf8
--- /dev/null
@@ -0,0 +1,115 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_SIMPLE_ASYNC_RESULT_H__
+#define __G_SIMPLE_ASYNC_RESULT_H__
+
+#include <gio/gasyncresult.h>
+#include <gio/gcancellable.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SIMPLE_ASYNC_RESULT         (g_simple_async_result_get_type ())
+#define G_SIMPLE_ASYNC_RESULT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SIMPLE_ASYNC_RESULT, GSimpleAsyncResult))
+#define G_SIMPLE_ASYNC_RESULT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_SIMPLE_ASYNC_RESULT, GSimpleAsyncResultClass))
+#define G_IS_SIMPLE_ASYNC_RESULT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SIMPLE_ASYNC_RESULT))
+#define G_IS_SIMPLE_ASYNC_RESULT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_SIMPLE_ASYNC_RESULT))
+#define G_SIMPLE_ASYNC_RESULT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_SIMPLE_ASYNC_RESULT, GSimpleAsyncResultClass))
+
+typedef struct _GSimpleAsyncResult        GSimpleAsyncResult;
+typedef struct _GSimpleAsyncResultClass   GSimpleAsyncResultClass;
+
+typedef void (*GSimpleAsyncThreadFunc) (GSimpleAsyncResult *res,
+                                       GObject *object,
+                                       GCancellable *cancellable);
+
+
+GType g_simple_async_result_get_type (void) G_GNUC_CONST;
+  
+GSimpleAsyncResult *g_simple_async_result_new              (GObject                 *source_object,
+                                                           GAsyncReadyCallback      callback,
+                                                           gpointer                 user_data,
+                                                           gpointer                 source_tag);
+GSimpleAsyncResult *g_simple_async_result_new_error        (GObject                 *source_object,
+                                                           GAsyncReadyCallback      callback,
+                                                           gpointer                 user_data,
+                                                           GQuark                   domain,
+                                                           gint                     code,
+                                                           const char              *format,
+                                                           ...) G_GNUC_PRINTF (6, 7);
+GSimpleAsyncResult *g_simple_async_result_new_from_error   (GObject                 *source_object,
+                                                           GAsyncReadyCallback      callback,
+                                                           gpointer                 user_data,
+                                                           GError                  *error);
+
+void                g_simple_async_result_set_op_res_gpointer (GSimpleAsyncResult      *simple,
+                                                               gpointer                 op_res,
+                                                               GDestroyNotify           destroy_op_res);
+gpointer            g_simple_async_result_get_op_res_gpointer (GSimpleAsyncResult      *simple);
+
+void                g_simple_async_result_set_op_res_gssize   (GSimpleAsyncResult      *simple,
+                                                               gssize                   op_res);
+gssize              g_simple_async_result_get_op_res_gssize   (GSimpleAsyncResult      *simple);
+
+void                g_simple_async_result_set_op_res_gboolean (GSimpleAsyncResult      *simple,
+                                                               gboolean                 op_res);
+gboolean            g_simple_async_result_get_op_res_gboolean (GSimpleAsyncResult      *simple);
+
+
+
+gpointer            g_simple_async_result_get_source_tag   (GSimpleAsyncResult      *simple);
+void                g_simple_async_result_set_handle_cancellation (GSimpleAsyncResult      *simple,
+                                                                  gboolean          handle_cancellation);
+void                g_simple_async_result_complete         (GSimpleAsyncResult      *simple);
+void                g_simple_async_result_complete_in_idle (GSimpleAsyncResult      *simple);
+void                g_simple_async_result_run_in_thread    (GSimpleAsyncResult      *simple,
+                                                           GSimpleAsyncThreadFunc   func,
+                                                           int                      io_priority,
+                                                           GCancellable            *cancellable);
+void                g_simple_async_result_set_from_error   (GSimpleAsyncResult      *simple,
+                                                           GError                  *error);
+gboolean            g_simple_async_result_propagate_error  (GSimpleAsyncResult      *simple,
+                                                           GError                 **dest);
+void                g_simple_async_result_set_error        (GSimpleAsyncResult      *simple,
+                                                           GQuark                   domain,
+                                                           gint                     code,
+                                                           const char              *format,
+                                                           ...) G_GNUC_PRINTF (4, 5);
+void                g_simple_async_result_set_error_va     (GSimpleAsyncResult      *simple,
+                                                           GQuark                   domain,
+                                                           gint                     code,
+                                                           const char              *format,
+                                                           va_list                  args);
+
+void g_simple_async_report_error_in_idle (GObject *object,
+                                         GAsyncReadyCallback callback,
+                                         gpointer user_data,
+                                         GQuark         domain,
+                                         gint           code,
+                                         const char    *format,
+                                         ...);
+
+G_END_DECLS
+
+
+  
+#endif /* __G_SIMPLE_ASYNC_RESULT_H__ */
diff --git a/gio/gsocketinputstream.c b/gio/gsocketinputstream.c
new file mode 100644 (file)
index 0000000..890e8e1
--- /dev/null
@@ -0,0 +1,469 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "gioerror.h"
+#include "gsimpleasyncresult.h"
+#include "gsocketinputstream.h"
+#include "gcancellable.h"
+#include "gasynchelper.h"
+
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GSocketInputStream, g_socket_input_stream, G_TYPE_INPUT_STREAM);
+
+struct _GSocketInputStreamPrivate {
+  int fd;
+  gboolean close_fd_at_close;
+};
+
+static gssize   g_socket_input_stream_read         (GInputStream         *stream,
+                                                   void                 *buffer,
+                                                   gsize                 count,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static gboolean g_socket_input_stream_close        (GInputStream         *stream,
+                                                   GCancellable         *cancellable,
+                                                   GError              **error);
+static void     g_socket_input_stream_read_async   (GInputStream         *stream,
+                                                   void                 *buffer,
+                                                   gsize                 count,
+                                                   int                   io_priority,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              data);
+static gssize   g_socket_input_stream_read_finish  (GInputStream         *stream,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
+static void     g_socket_input_stream_skip_async   (GInputStream         *stream,
+                                                   gsize                 count,
+                                                   int                   io_priority,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              data);
+static gssize   g_socket_input_stream_skip_finish  (GInputStream         *stream,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
+static void     g_socket_input_stream_close_async  (GInputStream         *stream,
+                                                   int                   io_priority,
+                                                   GCancellable         *cancellable,
+                                                   GAsyncReadyCallback   callback,
+                                                   gpointer              data);
+static gboolean g_socket_input_stream_close_finish (GInputStream         *stream,
+                                                   GAsyncResult         *result,
+                                                   GError              **error);
+
+static void
+g_socket_input_stream_finalize (GObject *object)
+{
+  GSocketInputStream *stream;
+  
+  stream = G_SOCKET_INPUT_STREAM (object);
+
+  if (G_OBJECT_CLASS (g_socket_input_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_socket_input_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_socket_input_stream_class_init (GSocketInputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GInputStreamClass *stream_class = G_INPUT_STREAM_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GSocketInputStreamPrivate));
+  
+  gobject_class->finalize = g_socket_input_stream_finalize;
+
+  stream_class->read = g_socket_input_stream_read;
+  stream_class->close = g_socket_input_stream_close;
+  stream_class->read_async = g_socket_input_stream_read_async;
+  stream_class->read_finish = g_socket_input_stream_read_finish;
+  if (0)
+    {
+      /* TODO: Implement instead of using fallbacks */
+      stream_class->skip_async = g_socket_input_stream_skip_async;
+      stream_class->skip_finish = g_socket_input_stream_skip_finish;
+    }
+  stream_class->close_async = g_socket_input_stream_close_async;
+  stream_class->close_finish = g_socket_input_stream_close_finish;
+}
+
+static void
+g_socket_input_stream_init (GSocketInputStream *socket)
+{
+  socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
+                                             G_TYPE_SOCKET_INPUT_STREAM,
+                                             GSocketInputStreamPrivate);
+}
+
+/**
+ * g_socket_input_stream_new:
+ * @fd: file descriptor.
+ * @close_fd_at_close: boolean value
+ * 
+ * 
+ * Returns: new #GInputStream. If @close_fd_at_close is %TRUE, 
+ * @fd will be closed when the #GInputStream is closed.
+ **/
+GInputStream *
+g_socket_input_stream_new (int fd,
+                          gboolean close_fd_at_close)
+{
+  GSocketInputStream *stream;
+
+  g_return_val_if_fail (fd != -1, NULL);
+
+  stream = g_object_new (G_TYPE_SOCKET_INPUT_STREAM, NULL);
+
+  stream->priv->fd = fd;
+  stream->priv->close_fd_at_close = close_fd_at_close;
+  
+  return G_INPUT_STREAM (stream);
+}
+
+static gssize
+g_socket_input_stream_read (GInputStream *stream,
+                           void         *buffer,
+                           gsize         count,
+                           GCancellable *cancellable,
+                           GError      **error)
+{
+  GSocketInputStream *socket_stream;
+  gssize res;
+  struct pollfd poll_fds[2];
+  int poll_ret;
+  int cancel_fd;
+
+  socket_stream = G_SOCKET_INPUT_STREAM (stream);
+
+  cancel_fd = g_cancellable_get_fd (cancellable);
+  if (cancel_fd != -1)
+    {
+      do
+       {
+         poll_fds[0].events = POLLIN;
+         poll_fds[0].fd = socket_stream->priv->fd;
+         poll_fds[1].events = POLLIN;
+         poll_fds[1].fd = cancel_fd;
+         poll_ret = poll (poll_fds, 2, -1);
+       }
+      while (poll_ret == -1 && errno == EINTR);
+      
+      if (poll_ret == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error reading from socket: %s"),
+                      g_strerror (errno));
+         return -1;
+       }
+    }
+
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       break;
+      res = read (socket_stream->priv->fd, buffer, count);
+      if (res == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error reading from socket: %s"),
+                      g_strerror (errno));
+       }
+      
+      break;
+    }
+
+  return res;
+}
+
+static gboolean
+g_socket_input_stream_close (GInputStream *stream,
+                            GCancellable *cancellable,
+                            GError      **error)
+{
+  GSocketInputStream *socket_stream;
+  int res;
+
+  socket_stream = G_SOCKET_INPUT_STREAM (stream);
+
+  if (!socket_stream->priv->close_fd_at_close)
+    return TRUE;
+  
+  while (1)
+    {
+      /* This might block during the close. Doesn't seem to be a way to avoid it though. */
+      res = close (socket_stream->priv->fd);
+      if (res == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error closing socket: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+  
+  return res != -1;
+}
+
+typedef struct {
+  gsize count;
+  void *buffer;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GCancellable *cancellable;
+  GSocketInputStream *stream;
+} ReadAsyncData;
+
+static gboolean
+read_async_cb (ReadAsyncData *data,
+              GIOCondition condition,
+              int fd)
+{
+  GSimpleAsyncResult *simple;
+  GError *error = NULL;
+  gssize count_read;
+
+  /* We know that we can read from fd once without blocking */
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+       {
+         count_read = -1;
+         break;
+       }
+      count_read = read (data->stream->priv->fd, data->buffer, data->count);
+      if (count_read == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (&error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error reading from socket: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+
+  simple = g_simple_async_result_new (G_OBJECT (data->stream),
+                                     data->callback,
+                                     data->user_data,
+                                     g_socket_input_stream_read_async);
+
+  g_simple_async_result_set_op_res_gssize (simple, count_read);
+
+  if (count_read == -1)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  return FALSE;
+}
+
+static void
+g_socket_input_stream_read_async (GInputStream        *stream,
+                                 void                *buffer,
+                                 gsize                count,
+                                 int                  io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             user_data)
+{
+  GSource *source;
+  GSocketInputStream *socket_stream;
+  ReadAsyncData *data;
+
+  socket_stream = G_SOCKET_INPUT_STREAM (stream);
+
+  data = g_new0 (ReadAsyncData, 1);
+  data->count = count;
+  data->buffer = buffer;
+  data->callback = callback;
+  data->user_data = user_data;
+  data->cancellable = cancellable;
+  data->stream = socket_stream;
+
+  source = _g_fd_source_new (socket_stream->priv->fd,
+                            POLLIN,
+                            cancellable);
+  
+  g_source_set_callback (source, (GSourceFunc)read_async_cb, data, g_free);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+}
+
+static gssize
+g_socket_input_stream_read_finish (GInputStream              *stream,
+                                  GAsyncResult              *result,
+                                  GError                   **error)
+{
+  GSimpleAsyncResult *simple;
+  gssize nread;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_socket_input_stream_read_async);
+  
+  nread = g_simple_async_result_get_op_res_gssize (simple);
+  return nread;
+}
+
+static void
+g_socket_input_stream_skip_async (GInputStream        *stream,
+                                 gsize                count,
+                                 int                  io_priority,
+                                 GCancellable        *cancellable,
+                                 GAsyncReadyCallback  callback,
+                                 gpointer             data)
+{
+  g_assert_not_reached ();
+  /* TODO: Not implemented */
+}
+
+static gssize
+g_socket_input_stream_skip_finish  (GInputStream              *stream,
+                                   GAsyncResult              *result,
+                                   GError                   **error)
+{
+  g_assert_not_reached ();
+  /* TODO: Not implemented */
+}
+
+
+typedef struct {
+  GInputStream *stream;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+} CloseAsyncData;
+
+static void
+close_async_data_free (gpointer _data)
+{
+  CloseAsyncData *data = _data;
+
+  g_free (data);
+}
+
+static gboolean
+close_async_cb (CloseAsyncData *data)
+{
+  GSocketInputStream *socket_stream;
+  GSimpleAsyncResult *simple;
+  GError *error = NULL;
+  gboolean result;
+  int res;
+
+  socket_stream = G_SOCKET_INPUT_STREAM (data->stream);
+
+  if (!socket_stream->priv->close_fd_at_close)
+    {
+      result = TRUE;
+      goto out;
+    }
+  
+  while (1)
+    {
+      res = close (socket_stream->priv->fd);
+      if (res == -1)
+       {
+         g_set_error (&error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error closing socket: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+  
+  result = res != -1;
+
+ out:
+  simple = g_simple_async_result_new (G_OBJECT (data->stream),
+                                     data->callback,
+                                     data->user_data,
+                                     g_socket_input_stream_close_async);
+
+  if (!result)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+  
+  return FALSE;
+}
+
+static void
+g_socket_input_stream_close_async (GInputStream       *stream,
+                                  int                 io_priority,
+                                  GCancellable       *cancellable,
+                                  GAsyncReadyCallback callback,
+                                  gpointer            user_data)
+{
+  GSource *idle;
+  CloseAsyncData *data;
+
+  data = g_new0 (CloseAsyncData, 1);
+
+  data->stream = stream;
+  data->callback = callback;
+  data->user_data = user_data;
+  
+  idle = g_idle_source_new ();
+  g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, close_async_data_free);
+  g_source_attach (idle, NULL);
+  g_source_unref (idle);
+}
+
+static gboolean
+g_socket_input_stream_close_finish (GInputStream              *stream,
+                                   GAsyncResult              *result,
+                                   GError                   **error)
+{
+  /* Failures handled in generic close_finish code */
+  return TRUE;
+}
+
diff --git a/gio/gsocketinputstream.h b/gio/gsocketinputstream.h
new file mode 100644 (file)
index 0000000..31b79ce
--- /dev/null
@@ -0,0 +1,68 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_SOCKET_INPUT_STREAM_H__
+#define __G_SOCKET_INPUT_STREAM_H__
+
+#include <gio/ginputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SOCKET_INPUT_STREAM         (g_socket_input_stream_get_type ())
+#define G_SOCKET_INPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStream))
+#define G_SOCKET_INPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStreamClass))
+#define G_IS_SOCKET_INPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SOCKET_INPUT_STREAM))
+#define G_IS_SOCKET_INPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_SOCKET_INPUT_STREAM))
+#define G_SOCKET_INPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_SOCKET_INPUT_STREAM, GSocketInputStreamClass))
+
+typedef struct _GSocketInputStream         GSocketInputStream;
+typedef struct _GSocketInputStreamClass    GSocketInputStreamClass;
+typedef struct _GSocketInputStreamPrivate  GSocketInputStreamPrivate;
+
+struct _GSocketInputStream
+{
+  GInputStream parent;
+
+  /*< private >*/
+  GSocketInputStreamPrivate *priv;
+};
+
+struct _GSocketInputStreamClass
+{
+  GInputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_socket_input_stream_get_type (void) G_GNUC_CONST;
+
+GInputStream *g_socket_input_stream_new (int fd,
+                                        gboolean close_fd_at_close);
+
+G_END_DECLS
+
+#endif /* __G_SOCKET_INPUT_STREAM_H__ */
diff --git a/gio/gsocketoutputstream.c b/gio/gsocketoutputstream.c
new file mode 100644 (file)
index 0000000..54539f5
--- /dev/null
@@ -0,0 +1,426 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include "gioerror.h"
+#include "gsocketoutputstream.h"
+#include "gcancellable.h"
+#include "gsimpleasyncresult.h"
+#include "gasynchelper.h"
+
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GSocketOutputStream, g_socket_output_stream, G_TYPE_OUTPUT_STREAM);
+
+
+struct _GSocketOutputStreamPrivate {
+  int fd;
+  gboolean close_fd_at_close;
+};
+
+static gssize   g_socket_output_stream_write        (GOutputStream              *stream,
+                                                    const void                 *buffer,
+                                                    gsize                       count,
+                                                    GCancellable               *cancellable,
+                                                    GError                    **error);
+static gboolean g_socket_output_stream_close        (GOutputStream              *stream,
+                                                    GCancellable               *cancellable,
+                                                    GError                    **error);
+static void     g_socket_output_stream_write_async  (GOutputStream              *stream,
+                                                    const void                 *buffer,
+                                                    gsize                       count,
+                                                    int                         io_priority,
+                                                    GCancellable               *cancellable,
+                                                    GAsyncReadyCallback         callback,
+                                                    gpointer                    data);
+static gssize   g_socket_output_stream_write_finish (GOutputStream              *stream,
+                                                    GAsyncResult               *result,
+                                                    GError                    **error);
+static void     g_socket_output_stream_close_async  (GOutputStream              *stream,
+                                                    int                         io_priority,
+                                                    GCancellable               *cancellable,
+                                                    GAsyncReadyCallback         callback,
+                                                    gpointer                    data);
+static gboolean g_socket_output_stream_close_finish (GOutputStream              *stream,
+                                                    GAsyncResult               *result,
+                                                    GError                    **error);
+
+
+static void
+g_socket_output_stream_finalize (GObject *object)
+{
+  GSocketOutputStream *stream;
+  
+  stream = G_SOCKET_OUTPUT_STREAM (object);
+
+  if (G_OBJECT_CLASS (g_socket_output_stream_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_socket_output_stream_parent_class)->finalize) (object);
+}
+
+static void
+g_socket_output_stream_class_init (GSocketOutputStreamClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GOutputStreamClass *stream_class = G_OUTPUT_STREAM_CLASS (klass);
+  
+  g_type_class_add_private (klass, sizeof (GSocketOutputStreamPrivate));
+  
+  gobject_class->finalize = g_socket_output_stream_finalize;
+
+  stream_class->write = g_socket_output_stream_write;
+  stream_class->close = g_socket_output_stream_close;
+  stream_class->write_async = g_socket_output_stream_write_async;
+  stream_class->write_finish = g_socket_output_stream_write_finish;
+  stream_class->close_async = g_socket_output_stream_close_async;
+  stream_class->close_finish = g_socket_output_stream_close_finish;
+}
+
+static void
+g_socket_output_stream_init (GSocketOutputStream *socket)
+{
+  socket->priv = G_TYPE_INSTANCE_GET_PRIVATE (socket,
+                                             G_TYPE_SOCKET_OUTPUT_STREAM,
+                                             GSocketOutputStreamPrivate);
+}
+
+
+/**
+ * g_socket_output_stream_new:
+ * @fd: file descriptor.
+ * @close_fd_at_close: boolean value.
+ * 
+ * Returns: #GOutputStream. If @close_fd_at_close is %TRUE, then
+ * @fd will be closed when the #GOutputStream is closed.
+ **/
+GOutputStream *
+g_socket_output_stream_new (int fd,
+                          gboolean close_fd_at_close)
+{
+  GSocketOutputStream *stream;
+
+  g_return_val_if_fail (fd != -1, NULL);
+
+  stream = g_object_new (G_TYPE_SOCKET_OUTPUT_STREAM, NULL);
+
+  stream->priv->fd = fd;
+  stream->priv->close_fd_at_close = close_fd_at_close;
+  
+  return G_OUTPUT_STREAM (stream);
+}
+
+static gssize
+g_socket_output_stream_write (GOutputStream *stream,
+                             const void    *buffer,
+                             gsize          count,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  GSocketOutputStream *socket_stream;
+  gssize res;
+  struct pollfd poll_fds[2];
+  int poll_ret;
+  int cancel_fd;
+
+  socket_stream = G_SOCKET_OUTPUT_STREAM (stream);
+
+  cancel_fd = g_cancellable_get_fd (cancellable);
+  if (cancel_fd != -1)
+    {
+      do
+       {
+         poll_fds[0].events = POLLOUT;
+         poll_fds[0].fd = socket_stream->priv->fd;
+         poll_fds[1].events = POLLIN;
+         poll_fds[1].fd = cancel_fd;
+         poll_ret = poll (poll_fds, 2, -1);
+       }
+      while (poll_ret == -1 && errno == EINTR);
+      
+      if (poll_ret == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error writing to socket: %s"),
+                      g_strerror (errno));
+         return -1;
+       }
+    }
+      
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (cancellable, error))
+       return -1;
+
+      res = write (socket_stream->priv->fd, buffer, count);
+      if (res == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error writing to socket: %s"),
+                      g_strerror (errno));
+       }
+      
+      break;
+    }
+  
+  return res;
+}
+
+static gboolean
+g_socket_output_stream_close (GOutputStream *stream,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  GSocketOutputStream *socket_stream;
+  int res;
+
+  socket_stream = G_SOCKET_OUTPUT_STREAM (stream);
+
+  if (!socket_stream->priv->close_fd_at_close)
+    return TRUE;
+  
+  while (1)
+    {
+      /* This might block during the close. Doesn't seem to be a way to avoid it though. */
+      res = close (socket_stream->priv->fd);
+      if (res == -1)
+       {
+         g_set_error (error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error closing socket: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+
+  return res != -1;
+}
+
+typedef struct {
+  gsize count;
+  const void *buffer;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+  GCancellable *cancellable;
+  GSocketOutputStream *stream;
+} WriteAsyncData;
+
+static gboolean
+write_async_cb (WriteAsyncData *data,
+               GIOCondition condition,
+               int fd)
+{
+  GSimpleAsyncResult *simple;
+  GError *error = NULL;
+  gssize count_written;
+
+  while (1)
+    {
+      if (g_cancellable_set_error_if_cancelled (data->cancellable, &error))
+       {
+         count_written = -1;
+         break;
+       }
+      
+      count_written = write (data->stream->priv->fd, data->buffer, data->count);
+      if (count_written == -1)
+       {
+         if (errno == EINTR)
+           continue;
+         
+         g_set_error (&error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error reading from socket: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+
+  simple = g_simple_async_result_new (G_OBJECT (data->stream),
+                                     data->callback,
+                                     data->user_data,
+                                     g_socket_output_stream_write_async);
+  
+  g_simple_async_result_set_op_res_gssize (simple, count_written);
+
+  if (count_written == -1)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+
+  return FALSE;
+}
+
+static void
+g_socket_output_stream_write_async (GOutputStream      *stream,
+                                   const void         *buffer,
+                                   gsize               count,
+                                   int                 io_priority,
+                                   GCancellable       *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer            user_data)
+{
+  GSource *source;
+  GSocketOutputStream *socket_stream;
+  WriteAsyncData *data;
+
+  socket_stream = G_SOCKET_OUTPUT_STREAM (stream);
+
+  data = g_new0 (WriteAsyncData, 1);
+  data->count = count;
+  data->buffer = buffer;
+  data->callback = callback;
+  data->user_data = user_data;
+  data->cancellable = cancellable;
+  data->stream = socket_stream;
+
+  source = _g_fd_source_new (socket_stream->priv->fd,
+                            POLLOUT,
+                            cancellable);
+  
+  g_source_set_callback (source, (GSourceFunc)write_async_cb, data, g_free);
+  g_source_attach (source, NULL);
+  
+  g_source_unref (source);
+}
+
+static gssize
+g_socket_output_stream_write_finish (GOutputStream *stream,
+                                    GAsyncResult *result,
+                                    GError **error)
+{
+  GSimpleAsyncResult *simple;
+  gssize nwritten;
+
+  simple = G_SIMPLE_ASYNC_RESULT (result);
+  g_assert (g_simple_async_result_get_source_tag (simple) == g_socket_output_stream_write_async);
+  
+  nwritten = g_simple_async_result_get_op_res_gssize (simple);
+  return nwritten;
+}
+
+typedef struct {
+  GOutputStream *stream;
+  GAsyncReadyCallback callback;
+  gpointer user_data;
+} CloseAsyncData;
+
+static gboolean
+close_async_cb (CloseAsyncData *data)
+{
+  GSocketOutputStream *socket_stream;
+  GSimpleAsyncResult *simple;
+  GError *error = NULL;
+  gboolean result;
+  int res;
+
+  socket_stream = G_SOCKET_OUTPUT_STREAM (data->stream);
+
+  if (!socket_stream->priv->close_fd_at_close)
+    {
+      result = TRUE;
+      goto out;
+    }
+  
+  while (1)
+    {
+      res = close (socket_stream->priv->fd);
+      if (res == -1)
+       {
+         g_set_error (&error, G_IO_ERROR,
+                      g_io_error_from_errno (errno),
+                      _("Error closing socket: %s"),
+                      g_strerror (errno));
+       }
+      break;
+    }
+  
+  result = res != -1;
+  
+ out:
+  simple = g_simple_async_result_new (G_OBJECT (data->stream),
+                                     data->callback,
+                                     data->user_data,
+                                     g_socket_output_stream_close_async);
+
+  if (!result)
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+    }
+
+  /* Complete immediately, not in idle, since we're already in a mainloop callout */
+  g_simple_async_result_complete (simple);
+  g_object_unref (simple);
+  
+  return FALSE;
+}
+
+static void
+g_socket_output_stream_close_async (GOutputStream       *stream,
+                                   int                 io_priority,
+                                   GCancellable       *cancellable,
+                                   GAsyncReadyCallback callback,
+                                   gpointer            user_data)
+{
+  GSource *idle;
+  CloseAsyncData *data;
+
+  data = g_new0 (CloseAsyncData, 1);
+
+  data->stream = stream;
+  data->callback = callback;
+  data->user_data = user_data;
+  
+  idle = g_idle_source_new ();
+  g_source_set_callback (idle, (GSourceFunc)close_async_cb, data, g_free);
+  g_source_attach (idle, NULL);
+  g_source_unref (idle);
+}
+
+static gboolean
+g_socket_output_stream_close_finish (GOutputStream              *stream,
+                                    GAsyncResult              *result,
+                                    GError                   **error)
+{
+  /* Failures handled in generic close_finish code */
+  return TRUE;
+}
diff --git a/gio/gsocketoutputstream.h b/gio/gsocketoutputstream.h
new file mode 100644 (file)
index 0000000..cff1986
--- /dev/null
@@ -0,0 +1,68 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_SOCKET_OUTPUT_STREAM_H__
+#define __G_SOCKET_OUTPUT_STREAM_H__
+
+#include <gio/goutputstream.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_SOCKET_OUTPUT_STREAM         (g_socket_output_stream_get_type ())
+#define G_SOCKET_OUTPUT_STREAM(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStream))
+#define G_SOCKET_OUTPUT_STREAM_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStreamClass))
+#define G_IS_SOCKET_OUTPUT_STREAM(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_SOCKET_OUTPUT_STREAM))
+#define G_IS_SOCKET_OUTPUT_STREAM_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_SOCKET_OUTPUT_STREAM))
+#define G_SOCKET_OUTPUT_STREAM_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_SOCKET_OUTPUT_STREAM, GSocketOutputStreamClass))
+
+typedef struct _GSocketOutputStream         GSocketOutputStream;
+typedef struct _GSocketOutputStreamClass    GSocketOutputStreamClass;
+typedef struct _GSocketOutputStreamPrivate  GSocketOutputStreamPrivate;
+
+struct _GSocketOutputStream
+{
+  GOutputStream parent;
+
+  /*< private >*/
+  GSocketOutputStreamPrivate *priv;
+};
+
+struct _GSocketOutputStreamClass
+{
+  GOutputStreamClass parent_class;
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+};
+
+GType g_socket_output_stream_get_type (void) G_GNUC_CONST;
+
+GOutputStream *g_socket_output_stream_new (int fd,
+                                          gboolean close_fd_at_close);
+
+G_END_DECLS
+
+#endif /* __G_SOCKET_OUTPUT_STREAM_H__ */
diff --git a/gio/gthemedicon.c b/gio/gthemedicon.c
new file mode 100644 (file)
index 0000000..e27b033
--- /dev/null
@@ -0,0 +1,172 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include "gthemedicon.h"
+
+static void g_themed_icon_icon_iface_init (GIconIface       *iface);
+
+struct _GThemedIcon
+{
+  GObject parent_instance;
+  
+  char **names;
+};
+
+struct _GThemedIconClass
+{
+  GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GThemedIcon, g_themed_icon, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ICON,
+                                               g_themed_icon_icon_iface_init))
+  
+static void
+g_themed_icon_finalize (GObject *object)
+{
+  GThemedIcon *themed;
+
+  themed = G_THEMED_ICON (object);
+
+  g_strfreev (themed->names);
+  
+  if (G_OBJECT_CLASS (g_themed_icon_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_themed_icon_parent_class)->finalize) (object);
+}
+
+static void
+g_themed_icon_class_init (GThemedIconClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_themed_icon_finalize;
+}
+
+static void
+g_themed_icon_init (GThemedIcon *themed)
+{
+}
+
+/**
+ * g_themed_icon_new:
+ * @iconname:
+ * 
+ * Returns: a new #GIcon.
+ **/
+GIcon *
+g_themed_icon_new (const char *iconname)
+{
+  GThemedIcon *themed;
+
+  g_return_val_if_fail (iconname != NULL, NULL);
+
+  themed = g_object_new (G_TYPE_THEMED_ICON, NULL);
+  themed->names = g_new (char *, 2);
+  themed->names[0] = g_strdup (iconname);
+  themed->names[1] = NULL;
+  
+  return G_ICON (themed);
+}
+
+/**
+ * g_themed_icon_new_from_names:
+ * @iconnames:
+ * @len:
+ * 
+ * Returns: a new #GIcon.
+ **/
+GIcon *
+g_themed_icon_new_from_names (char **iconnames, int len)
+{
+  GThemedIcon *themed;
+  int i;
+
+  g_return_val_if_fail (iconnames != NULL, NULL);
+  
+  themed = g_object_new (G_TYPE_THEMED_ICON, NULL);
+  if (len == -1)
+    themed->names = g_strdupv (iconnames);
+  else
+    {
+      themed->names = g_new (char *, len + 1);
+      for (i = 0; i < len; i++)
+       themed->names[i] = g_strdup (iconnames[i]);
+      themed->names[i] = NULL;
+    }
+  
+  
+  return G_ICON (themed);
+}
+
+/**
+ * g_themed_icon_get_names:
+ * @icon:
+ * 
+ * Returns: 
+ **/
+const char * const *
+g_themed_icon_get_names (GThemedIcon *icon)
+{
+  g_return_val_if_fail (G_IS_THEMED_ICON (icon), NULL);
+  return (const char * const *)icon->names;
+}
+
+static guint
+g_themed_icon_hash (GIcon *icon)
+{
+  GThemedIcon *themed = G_THEMED_ICON (icon);
+  guint hash;
+  int i;
+
+  hash = 0;
+
+  for (i = 0; themed->names[i] != NULL; i++)
+    hash ^= g_str_hash (themed->names[i]);
+  
+  return hash;
+}
+
+static gboolean
+g_themed_icon_equal (GIcon *icon1,
+                   GIcon *icon2)
+{
+  GThemedIcon *themed1 = G_THEMED_ICON (icon1);
+  GThemedIcon *themed2 = G_THEMED_ICON (icon2);
+  int i;
+
+  for (i = 0; themed1->names[i] != NULL && themed2->names[i] != NULL; i++)
+    {
+      if (!g_str_equal (themed1->names[i], themed2->names[i]))
+       return FALSE;
+    }
+
+  return themed1->names[i] == NULL && themed2->names[i] == NULL;
+}
+
+static void
+g_themed_icon_icon_iface_init (GIconIface *iface)
+{
+  iface->hash = g_themed_icon_hash;
+  iface->equal = g_themed_icon_equal;
+}
diff --git a/gio/gthemedicon.h b/gio/gthemedicon.h
new file mode 100644 (file)
index 0000000..f560f18
--- /dev/null
@@ -0,0 +1,49 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_THEMED_ICON_H__
+#define __G_THEMED_ICON_H__
+
+#include <gio/gicon.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_THEMED_ICON         (g_themed_icon_get_type ())
+#define G_THEMED_ICON(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_THEMED_ICON, GThemedIcon))
+#define G_THEMED_ICON_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_THEMED_ICON, GThemedIconClass))
+#define G_IS_THEMED_ICON(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_THEMED_ICON))
+#define G_IS_THEMED_ICON_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_THEMED_ICON))
+#define G_THEMED_ICON_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_THEMED_ICON, GThemedIconClass))
+
+typedef struct _GThemedIcon        GThemedIcon;
+typedef struct _GThemedIconClass   GThemedIconClass;
+
+GType g_themed_icon_get_type (void) G_GNUC_CONST;
+  
+GIcon *g_themed_icon_new (const char *iconname);
+GIcon *g_themed_icon_new_from_names (char **iconnames, int len);
+
+const char * const *g_themed_icon_get_names (GThemedIcon *icon);
+
+G_END_DECLS
+
+#endif /* __G_THEMED_ICON_H__ */
diff --git a/gio/gunionvolumemonitor.c b/gio/gunionvolumemonitor.c
new file mode 100644 (file)
index 0000000..bc46d4c
--- /dev/null
@@ -0,0 +1,392 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include "gunionvolumemonitor.h"
+#include "gvolumeprivate.h"
+#include "giomodule.h"
+#ifdef G_OS_UNIX
+#include "gunixvolumemonitor.h"
+#endif
+#include "gnativevolumemonitor.h"
+
+#include "glibintl.h"
+
+struct _GUnionVolumeMonitor {
+  GVolumeMonitor parent;
+
+  GList *monitors;
+};
+
+static void g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
+                                                  GVolumeMonitor *child_monitor);
+
+
+G_DEFINE_TYPE (GUnionVolumeMonitor, g_union_volume_monitor, G_TYPE_VOLUME_MONITOR);
+
+
+G_LOCK_DEFINE_STATIC(the_volume_monitor);
+static GUnionVolumeMonitor *the_volume_monitor = NULL;
+
+static void
+g_union_volume_monitor_finalize (GObject *object)
+{
+  GUnionVolumeMonitor *monitor;
+  
+  monitor = G_UNION_VOLUME_MONITOR (object);
+
+  while (monitor->monitors != NULL)
+    g_union_volume_monitor_remove_monitor (monitor,
+                                          monitor->monitors->data);
+  
+  if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->finalize) (object);
+}
+
+static void
+g_union_volume_monitor_dispose (GObject *object)
+{
+  GUnionVolumeMonitor *monitor;
+  
+  monitor = G_UNION_VOLUME_MONITOR (object);
+
+  G_LOCK (the_volume_monitor);
+  the_volume_monitor = NULL;
+  G_UNLOCK (the_volume_monitor);
+  
+  if (G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose)
+    (*G_OBJECT_CLASS (g_union_volume_monitor_parent_class)->dispose) (object);
+}
+
+static GList *
+get_mounted_volumes (GVolumeMonitor *volume_monitor)
+{
+  GUnionVolumeMonitor *monitor;
+  GVolumeMonitor *child_monitor;
+  GList *res;
+  GList *l;
+  
+  monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
+
+  res = NULL;
+  
+  G_LOCK (the_volume_monitor);
+
+  for (l = monitor->monitors; l != NULL; l = l->next)
+    {
+      child_monitor = l->data;
+
+      res = g_list_concat (res,
+                          g_volume_monitor_get_mounted_volumes (child_monitor));
+    }
+  
+  G_UNLOCK (the_volume_monitor);
+
+  return res;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+  GUnionVolumeMonitor *monitor;
+  GVolumeMonitor *child_monitor;
+  GList *res;
+  GList *l;
+  
+  monitor = G_UNION_VOLUME_MONITOR (volume_monitor);
+
+  res = NULL;
+  
+  G_LOCK (the_volume_monitor);
+
+  for (l = monitor->monitors; l != NULL; l = l->next)
+    {
+      child_monitor = l->data;
+
+      res = g_list_concat (res,
+                          g_volume_monitor_get_connected_drives (child_monitor));
+    }
+  
+  G_UNLOCK (the_volume_monitor);
+
+  return res;
+}
+
+static void
+g_union_volume_monitor_class_init (GUnionVolumeMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_union_volume_monitor_finalize;
+  gobject_class->dispose = g_union_volume_monitor_dispose;
+
+  monitor_class->get_mounted_volumes = get_mounted_volumes;
+  monitor_class->get_connected_drives = get_connected_drives;
+}
+
+static void
+child_volume_mounted (GVolumeMonitor *child_monitor,
+                     GVolume *child_volume,
+                     GUnionVolumeMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                        "volume_mounted",
+                        child_volume);
+}
+
+static void
+child_volume_pre_unmount (GVolumeMonitor *child_monitor,
+                         GVolume *child_volume,
+                         GUnionVolumeMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                        "volume_pre_unmount",
+                        child_volume);
+}
+
+static void
+child_volume_unmounted (GVolumeMonitor *child_monitor,
+                       GVolume *child_volume,
+                       GUnionVolumeMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                        "volume_unmounted",
+                        child_volume);
+}
+
+static void
+child_drive_connected (GVolumeMonitor *child_monitor,
+                      GDrive *child_drive,
+                      GUnionVolumeMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                        "drive_connected",
+                        child_drive);
+}
+
+static void
+child_drive_disconnected (GVolumeMonitor *child_monitor,
+                         GDrive *child_drive,
+                         GUnionVolumeMonitor *union_monitor)
+{
+  g_signal_emit_by_name (union_monitor,
+                        "drive_disconnected",
+                        child_drive);
+}
+
+static void
+g_union_volume_monitor_add_monitor (GUnionVolumeMonitor *union_monitor,
+                                   GVolumeMonitor *volume_monitor)
+{
+  if (g_list_find (union_monitor->monitors, volume_monitor))
+    return;
+
+  union_monitor->monitors =
+    g_list_prepend (union_monitor->monitors,
+                   g_object_ref (volume_monitor));
+
+  g_signal_connect (volume_monitor, "volume_mounted", (GCallback)child_volume_mounted, union_monitor);
+  g_signal_connect (volume_monitor, "volume_pre_unmount", (GCallback)child_volume_pre_unmount, union_monitor);
+  g_signal_connect (volume_monitor, "volume_unmounted", (GCallback)child_volume_unmounted, union_monitor);
+  g_signal_connect (volume_monitor, "drive_connected", (GCallback)child_drive_connected, union_monitor);
+  g_signal_connect (volume_monitor, "drive_disconnected", (GCallback)child_drive_disconnected, union_monitor);
+}
+
+static void
+g_union_volume_monitor_remove_monitor (GUnionVolumeMonitor *union_monitor,
+                                      GVolumeMonitor *child_monitor)
+{
+  GList *l;
+
+  l = g_list_find (union_monitor->monitors, child_monitor);
+  if (l == NULL)
+    return;
+
+  union_monitor->monitors = g_list_delete_link (union_monitor->monitors, l);
+
+  g_signal_handlers_disconnect_by_func (child_monitor, child_volume_mounted, union_monitor);
+  g_signal_handlers_disconnect_by_func (child_monitor, child_volume_pre_unmount, union_monitor);
+  g_signal_handlers_disconnect_by_func (child_monitor, child_volume_unmounted, union_monitor);
+  g_signal_handlers_disconnect_by_func (child_monitor, child_drive_connected, union_monitor);
+  g_signal_handlers_disconnect_by_func (child_monitor, child_drive_disconnected, union_monitor);
+}
+
+static gpointer
+get_default_native_type (gpointer data)
+{
+  GNativeVolumeMonitorClass *klass;
+  GType *monitors;
+  guint n_monitors;
+  GType native_type;
+  GType *ret = (GType *) data;
+  int native_prio;
+  int i;
+      
+#ifdef G_OS_UNIX
+  /* Ensure GUnixVolumeMonitor type is available */
+  {
+    volatile GType unix_type;
+    /* volatile is required to avoid any G_GNUC_CONST optimizations */
+    unix_type = g_unix_volume_monitor_get_type ();
+  }
+#endif
+      
+  /* Ensure vfs in modules loaded */
+  g_io_modules_ensure_loaded (GIO_MODULE_DIR);
+      
+  monitors = g_type_children (G_TYPE_NATIVE_VOLUME_MONITOR, &n_monitors);
+  native_type = 0;
+  native_prio = -1;
+  
+  for (i = 0; i < n_monitors; i++)
+    {
+      klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (monitors[i]));
+      if (klass->priority > native_prio)
+        {
+         native_prio = klass->priority;
+         native_type = monitors[i];
+       }
+
+      g_type_class_unref (klass);
+    }
+      
+  g_free (monitors);
+
+  *ret = native_type;
+
+  return NULL;
+}
+
+static GType
+get_native_type (void)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  static GType type = G_TYPE_INVALID;
+
+  g_once (&once_init, get_default_native_type, &type);
+  
+  return type;
+}
+
+static void
+g_union_volume_monitor_init (GUnionVolumeMonitor *union_monitor)
+{
+  GVolumeMonitor *monitor;
+  GType *monitors;
+  guint n_monitors;
+  GType native_type;
+  int i;
+
+  native_type = get_native_type ();
+
+  if (native_type != G_TYPE_INVALID)
+    {
+      monitor = g_object_new (native_type, NULL);
+      g_union_volume_monitor_add_monitor (union_monitor, monitor);
+      g_object_unref (monitor);
+    }
+  
+  monitors = g_type_children (G_TYPE_VOLUME_MONITOR, &n_monitors);
+  
+  for (i = 0; i < n_monitors; i++)
+    {
+      if (monitors[i] == G_TYPE_UNION_VOLUME_MONITOR ||
+         g_type_is_a (monitors[i], G_TYPE_NATIVE_VOLUME_MONITOR))
+       continue;
+      
+      monitor = g_object_new (monitors[i], NULL);
+      g_union_volume_monitor_add_monitor (union_monitor, monitor);
+      g_object_unref (monitor);
+    }
+      
+  g_free (monitors);
+}
+
+static GUnionVolumeMonitor *
+g_union_volume_monitor_new (void)
+{
+  GUnionVolumeMonitor *monitor;
+
+  monitor = g_object_new (G_TYPE_UNION_VOLUME_MONITOR, NULL);
+  
+  return monitor;
+}
+
+
+/**
+ * g_volume_monitor_get:
+ * 
+ * Returns: a #GVolumeMonitor.
+ **/
+GVolumeMonitor *
+g_volume_monitor_get (void)
+{
+  GVolumeMonitor *vm;
+  
+  G_LOCK (the_volume_monitor);
+
+  if (the_volume_monitor )
+    vm = G_VOLUME_MONITOR (g_object_ref (the_volume_monitor));
+  else
+    {
+      the_volume_monitor = g_union_volume_monitor_new ();
+      vm = G_VOLUME_MONITOR (the_volume_monitor);
+    }
+  
+  G_UNLOCK (the_volume_monitor);
+
+  return vm;
+}
+
+/**
+ * g_volume_get_for_mount_path:
+ * @mountpoint: a string.
+ * 
+ * Returns: a #GVolume for given @mountpoint or %NULL.  
+ **/
+GVolume *
+g_volume_get_for_mount_path (const char *mountpoint)
+{
+  GType native_type;
+  GNativeVolumeMonitorClass *klass;
+  GVolume *volume;
+  
+  native_type = get_native_type ();
+
+  if (native_type == G_TYPE_INVALID)
+    return NULL;
+
+  volume = NULL;
+  
+  klass = G_NATIVE_VOLUME_MONITOR_CLASS (g_type_class_ref (native_type));
+  if (klass->get_volume_for_mountpoint)
+    volume = klass->get_volume_for_mountpoint (mountpoint);
+  
+  g_type_class_unref (klass);
+
+  return volume;
+}
diff --git a/gio/gunionvolumemonitor.h b/gio/gunionvolumemonitor.h
new file mode 100644 (file)
index 0000000..8cb3ecb
--- /dev/null
@@ -0,0 +1,54 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_UNION_VOLUME_MONITOR_H__
+#define __G_UNION_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gvolumemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_UNION_VOLUME_MONITOR        (g_union_volume_monitor_get_type ())
+#define G_UNION_VOLUME_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNION_VOLUME_MONITOR, GUnionVolumeMonitor))
+#define G_UNION_VOLUME_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNION_VOLUME_MONITOR, GUnionVolumeMonitorClass))
+#define G_IS_UNION_VOLUME_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNION_VOLUME_MONITOR))
+#define G_IS_UNION_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNION_VOLUME_MONITOR))
+
+typedef struct _GUnionVolumeMonitor GUnionVolumeMonitor;
+typedef struct _GUnionVolumeMonitorClass GUnionVolumeMonitorClass;
+
+struct _GUnionVolumeMonitorClass {
+  GVolumeMonitorClass parent_class;
+
+};
+
+GType g_union_volume_monitor_get_type (void) G_GNUC_CONST;
+
+GList * g_union_volume_monitor_convert_volumes (GUnionVolumeMonitor *monitor,
+                                               GList               *child_volumes);
+GDrive *g_union_volume_monitor_convert_drive   (GUnionVolumeMonitor *monitor,
+                                               GDrive              *child_drive);
+
+G_END_DECLS
+
+#endif /* __G_UNION_VOLUME_MONITOR_H__ */
diff --git a/gio/gunixdrive.c b/gio/gunixdrive.c
new file mode 100644 (file)
index 0000000..4643992
--- /dev/null
@@ -0,0 +1,321 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include "gunixdrive.h"
+#include "gunixvolume.h"
+#include "gdriveprivate.h"
+#include "gthemedicon.h"
+#include "gvolumemonitor.h"
+
+#include "glibintl.h"
+
+struct _GUnixDrive {
+  GObject parent;
+
+  GUnixVolume *volume; /* owned by volume monitor */
+  char *name;
+  char *icon;
+  char *mountpoint;
+  GUnixMountType guessed_type;
+};
+
+static void g_unix_volume_drive_iface_init (GDriveIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GUnixDrive, g_unix_drive, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_DRIVE,
+                                               g_unix_volume_drive_iface_init))
+
+static void
+g_unix_drive_finalize (GObject *object)
+{
+  GUnixDrive *drive;
+  
+  drive = G_UNIX_DRIVE (object);
+
+  if (drive->volume)
+    g_unix_volume_unset_drive (drive->volume, drive);
+  
+  g_free (drive->name);
+  g_free (drive->icon);
+  g_free (drive->mountpoint);
+
+  if (G_OBJECT_CLASS (g_unix_drive_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_unix_drive_parent_class)->finalize) (object);
+}
+
+static void
+g_unix_drive_class_init (GUnixDriveClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_unix_drive_finalize;
+}
+
+static void
+g_unix_drive_init (GUnixDrive *unix_drive)
+{
+}
+
+static char *
+type_to_icon (GUnixMountType type)
+{
+  const char *icon_name = NULL;
+  
+  switch (type)
+    {
+    case G_UNIX_MOUNT_TYPE_HD:
+      icon_name = "drive-harddisk";
+      break;
+    case G_UNIX_MOUNT_TYPE_FLOPPY:
+    case G_UNIX_MOUNT_TYPE_ZIP:
+    case G_UNIX_MOUNT_TYPE_JAZ:
+    case G_UNIX_MOUNT_TYPE_MEMSTICK:
+      icon_name = "drive-removable-media";
+      break;
+    case G_UNIX_MOUNT_TYPE_CDROM:
+      icon_name = "drive-optical";
+      break;
+    case G_UNIX_MOUNT_TYPE_NFS:
+      /* TODO: Would like a better icon here... */
+      icon_name = "drive-removable-media";
+      break;
+    case G_UNIX_MOUNT_TYPE_CAMERA:
+      icon_name = "camera-photo";
+      break;
+    case G_UNIX_MOUNT_TYPE_IPOD:
+      icon_name = "multimedia-player";
+      break;
+    case G_UNIX_MOUNT_TYPE_UNKNOWN:
+    default:
+      icon_name = "drive-removable-media";
+      break;
+    }
+  return g_strdup (icon_name);
+}
+
+/**
+ * g_unix_drive_new:
+ * @volume_monitor: a #GVolumeMonitor.
+ * @mountpoint: a #GUnixMountPoint.
+ * 
+ * Returns: a #GUnixDrive for the given #GUnixMountPoint.
+ **/
+GUnixDrive *
+g_unix_drive_new (GVolumeMonitor *volume_monitor,
+                 GUnixMountPoint *mountpoint)
+{
+  GUnixDrive *drive;
+  
+  if (!(g_unix_mount_point_is_user_mountable (mountpoint) ||
+       g_str_has_prefix (g_unix_mount_point_get_device_path (mountpoint), "/vol/")) ||
+      g_unix_mount_point_is_loopback (mountpoint))
+    return NULL;
+  
+  drive = g_object_new (G_TYPE_UNIX_DRIVE, NULL);
+
+  drive->guessed_type = g_unix_mount_point_guess_type (mountpoint);
+  
+  /* TODO: */
+  drive->mountpoint = g_strdup (g_unix_mount_point_get_mount_path (mountpoint));
+  drive->icon = type_to_icon (drive->guessed_type);
+  drive->name = g_strdup (_("Unknown drive"));
+  
+  return drive;
+}
+
+/**
+ * g_unix_drive_disconnected:
+ * @drive:
+ * 
+ **/
+void
+g_unix_drive_disconnected (GUnixDrive *drive)
+{
+  if (drive->volume)
+    {
+      g_unix_volume_unset_drive (drive->volume, drive);
+      drive->volume = NULL;
+    }
+}
+
+/**
+ * g_unix_drive_set_volume:
+ * @drive:
+ * @volume:
+ *  
+ **/
+void
+g_unix_drive_set_volume (GUnixDrive     *drive,
+                        GUnixVolume    *volume)
+{
+  if (drive->volume == volume)
+    return;
+  
+  if (drive->volume)
+    g_unix_volume_unset_drive (drive->volume, drive);
+  
+  drive->volume = volume;
+  
+  /* TODO: Emit changed in idle to avoid locking issues */
+  g_signal_emit_by_name (drive, "changed");
+}
+
+/**
+ * g_unix_drive_unset_volume:
+ * @drive:
+ * @volume:
+ *
+ **/
+void
+g_unix_drive_unset_volume (GUnixDrive     *drive,
+                          GUnixVolume    *volume)
+{
+  if (drive->volume == volume)
+    {
+      drive->volume = NULL;
+      /* TODO: Emit changed in idle to avoid locking issues */
+      g_signal_emit_by_name (drive, "changed");
+    }
+}
+
+static GIcon *
+g_unix_drive_get_icon (GDrive *drive)
+{
+  GUnixDrive *unix_drive = G_UNIX_DRIVE (drive);
+
+  return g_themed_icon_new (unix_drive->icon);
+}
+
+static char *
+g_unix_drive_get_name (GDrive *drive)
+{
+  GUnixDrive *unix_drive = G_UNIX_DRIVE (drive);
+  
+  return g_strdup (unix_drive->name);
+}
+
+static gboolean
+g_unix_drive_is_automounted (GDrive *drive)
+{
+  /* TODO */
+  return FALSE;
+}
+
+static gboolean
+g_unix_drive_can_mount (GDrive *drive)
+{
+  /* TODO */
+  return TRUE;
+}
+
+static gboolean
+g_unix_drive_can_eject (GDrive *drive)
+{
+  /* TODO */
+  return FALSE;
+}
+
+static GList *
+g_unix_drive_get_volumes (GDrive *drive)
+{
+  GList *l;
+  GUnixDrive *unix_drive = G_UNIX_DRIVE (drive);
+
+  l = NULL;
+  if (unix_drive->volume)
+    l = g_list_prepend (l, g_object_ref (unix_drive->volume));
+
+  return l;
+}
+
+static gboolean
+g_unix_drive_has_volumes (GDrive *drive)
+{
+  GUnixDrive *unix_drive = G_UNIX_DRIVE (drive);
+
+  return unix_drive->volume != NULL;
+}
+
+
+gboolean
+g_unix_drive_has_mountpoint (GUnixDrive *drive,
+                            const char  *mountpoint)
+{
+  return strcmp (drive->mountpoint, mountpoint) == 0;
+}
+
+static void
+g_unix_drive_mount (GDrive         *drive,
+                   GMountOperation *mount_operation,
+                   GCancellable *cancellable,
+                   GAsyncReadyCallback callback,
+                   gpointer        user_data)
+{
+  /* TODO */
+}
+
+
+static gboolean
+g_unix_drive_mount_finish (GDrive *drive,
+                          GAsyncResult *result,
+                          GError **error)
+{
+  return TRUE;
+}
+
+static void
+g_unix_drive_eject (GDrive         *drive,
+                   GCancellable *cancellable,
+                   GAsyncReadyCallback callback,
+                   gpointer        user_data)
+{
+  /* TODO */
+}
+
+static gboolean
+g_unix_drive_eject_finish (GDrive *drive,
+                          GAsyncResult *result,
+                          GError **error)
+{
+  return TRUE;
+}
+
+static void
+g_unix_volume_drive_iface_init (GDriveIface *iface)
+{
+  iface->get_name = g_unix_drive_get_name;
+  iface->get_icon = g_unix_drive_get_icon;
+  iface->has_volumes = g_unix_drive_has_volumes;
+  iface->get_volumes = g_unix_drive_get_volumes;
+  iface->is_automounted = g_unix_drive_is_automounted;
+  iface->can_mount = g_unix_drive_can_mount;
+  iface->can_eject = g_unix_drive_can_eject;
+  iface->mount = g_unix_drive_mount;
+  iface->mount_finish = g_unix_drive_mount_finish;
+  iface->eject = g_unix_drive_eject;
+  iface->eject_finish = g_unix_drive_eject_finish;
+}
diff --git a/gio/gunixdrive.h b/gio/gunixdrive.h
new file mode 100644 (file)
index 0000000..869ed84
--- /dev/null
@@ -0,0 +1,59 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_UNIX_DRIVE_H__
+#define __G_UNIX_DRIVE_H__
+
+#include <glib-object.h>
+#include <gio/gdrive.h>
+#include <gio/gunixmounts.h>
+#include <gio/gunixvolumemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_UNIX_DRIVE        (g_unix_drive_get_type ())
+#define G_UNIX_DRIVE(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_DRIVE, GUnixDrive))
+#define G_UNIX_DRIVE_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNIX_DRIVE, GUnixDriveClass))
+#define G_IS_UNIX_DRIVE(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_DRIVE))
+#define G_IS_UNIX_DRIVE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNIX_DRIVE))
+
+typedef struct _GUnixDriveClass GUnixDriveClass;
+
+struct _GUnixDriveClass {
+   GObjectClass parent_class;
+};
+
+GType g_unix_drive_get_type (void) G_GNUC_CONST;
+
+GUnixDrive *g_unix_drive_new            (GVolumeMonitor *volume_monitor,
+                                        GUnixMountPoint *mountpoint);
+gboolean    g_unix_drive_has_mountpoint (GUnixDrive     *drive,
+                                        const char     *mountpoint);
+void        g_unix_drive_set_volume     (GUnixDrive     *drive,
+                                        GUnixVolume    *volume);
+void        g_unix_drive_unset_volume   (GUnixDrive     *drive,
+                                        GUnixVolume    *volume);
+void        g_unix_drive_disconnected   (GUnixDrive     *drive);
+
+G_END_DECLS
+
+#endif /* __G_UNIX_DRIVE_H__ */
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
new file mode 100644 (file)
index 0000000..da23bd4
--- /dev/null
@@ -0,0 +1,1515 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#ifndef HAVE_SYSCTLBYNAME
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+#include <sys/poll.h>
+#endif
+#endif
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+
+#include "gunixmounts.h"
+#include "gfile.h"
+#include "gfilemonitor.h"
+
+struct _GUnixMount {
+  char *mount_path;
+  char *device_path;
+  char *filesystem_type;
+  gboolean is_read_only;
+  gboolean is_system_internal;
+};
+
+struct _GUnixMountPoint {
+  char *mount_path;
+  char *device_path;
+  char *filesystem_type;
+  gboolean is_read_only;
+  gboolean is_user_mountable;
+  gboolean is_loopback;
+};
+
+enum {
+  MOUNTS_CHANGED,
+  MOUNTPOINTS_CHANGED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+struct _GUnixMountMonitor {
+  GObject parent;
+
+  GFileMonitor *fstab_monitor;
+  GFileMonitor *mtab_monitor;
+};
+
+struct _GUnixMountMonitorClass {
+  GObjectClass parent_class;
+};
+  
+static GUnixMountMonitor *the_mount_monitor = NULL;
+
+static GList *_g_get_unix_mounts (void);
+static GList *_g_get_unix_mount_points (void);
+
+G_DEFINE_TYPE (GUnixMountMonitor, g_unix_mount_monitor, G_TYPE_OBJECT);
+
+#define MOUNT_POLL_INTERVAL 4000
+
+#ifdef HAVE_SYS_MNTTAB_H
+#define MNTOPT_RO      "ro"
+#endif
+
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#elif defined (HAVE_SYS_MNTTAB_H)
+#include <sys/mnttab.h>
+#endif
+
+#ifdef HAVE_SYS_VFSTAB_H
+#include <sys/vfstab.h>
+#endif
+
+#if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
+#include <sys/mntctl.h>
+#include <sys/vfs.h>
+#include <sys/vmount.h>
+#include <fshelp.h>
+#endif
+
+#if defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <fstab.h>
+#ifdef HAVE_SYS_SYSCTL_H
+#include <sys/sysctl.h>
+#endif
+#endif
+
+#ifndef HAVE_SETMNTENT
+#define setmntent(f,m) fopen(f,m)
+#endif
+#ifndef HAVE_ENDMNTENT
+#define endmntent(f) fclose(f)
+#endif
+
+static gboolean
+is_in (const char *value, const char *set[])
+{
+  int i;
+  for (i = 0; set[i] != NULL; i++)
+    {
+      if (strcmp (set[i], value) == 0)
+       return TRUE;
+    }
+  return FALSE;
+}
+
+static gboolean
+guess_system_internal (const char *mountpoint,
+                      const char *fs,
+                      const char *device)
+{
+  const char *ignore_fs[] = {
+    "auto",
+    "autofs",
+    "devfs",
+    "devpts",
+    "kernfs",
+    "linprocfs",
+    "proc",
+    "procfs",
+    "ptyfs",
+    "rootfs",
+    "selinuxfs",
+    "sysfs",
+    "tmpfs",
+    "usbfs",
+    "nfsd",
+    NULL
+  };
+  const char *ignore_devices[] = {
+    "none",
+    "sunrpc",
+    "devpts",
+    "nfsd",
+    "/dev/loop",
+    "/dev/vn",
+    NULL
+  };
+  const char *ignore_mountpoints[] = {
+    /* Includes all FHS 2.3 toplevel dirs */
+    "/bin",
+    "/boot",
+    "/dev",
+    "/etc",
+    "/home",
+    "/lib",
+    "/lib64",
+    "/media",
+    "/mnt",
+    "/opt",
+    "/root",
+    "/sbin",
+    "/srv",
+    "/tmp",
+    "/usr",
+    "/var",
+    "/proc",
+    "/sbin",
+    "/net",
+    NULL
+  };
+  
+  if (is_in (fs, ignore_fs))
+    return TRUE;
+  
+  if (is_in (device, ignore_devices))
+    return TRUE;
+
+  if (is_in (mountpoint, ignore_mountpoints))
+    return TRUE;
+  
+  if (g_str_has_prefix (mountpoint, "/dev") ||
+      g_str_has_prefix (mountpoint, "/proc") ||
+      g_str_has_prefix (mountpoint, "/sys"))
+    return TRUE;
+
+  if (strstr (mountpoint, "/.gvfs") != NULL)
+    return TRUE;
+  
+  return FALSE;
+}
+
+#ifdef HAVE_MNTENT_H
+
+static char *
+get_mtab_read_file (void)
+{
+#ifdef _PATH_MOUNTED
+# ifdef __linux__
+  return "/proc/mounts";
+# else
+  return _PATH_MOUNTED;
+# endif
+#else  
+  return "/etc/mtab";
+#endif
+}
+
+static char *
+get_mtab_monitor_file (void)
+{
+#ifdef _PATH_MOUNTED
+  return _PATH_MOUNTED;
+#else  
+  return "/etc/mtab";
+#endif
+}
+
+G_LOCK_DEFINE_STATIC(getmntent);
+
+static GList *
+_g_get_unix_mounts ()
+{
+  struct mntent *mntent;
+  FILE *file;
+  char *read_file;
+  GUnixMount *mount_entry;
+  GHashTable *mounts_hash;
+  GList *return_list;
+  
+  read_file = get_mtab_read_file ();
+
+  file = setmntent (read_file, "r");
+  if (file == NULL)
+    return NULL;
+
+  return_list = NULL;
+  
+  mounts_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  
+  G_LOCK (getmntent);
+  while ((mntent = getmntent (file)) != NULL)
+    {
+      /* ignore any mnt_fsname that is repeated and begins with a '/'
+       *
+       * We do this to avoid being fooled by --bind mounts, since
+       * these have the same device as the location they bind to.
+       * Its not an ideal solution to the problem, but its likely that
+       * the most important mountpoint is first and the --bind ones after
+       * that aren't as important. So it should work.
+       *
+       * The '/' is to handle procfs, tmpfs and other no device mounts.
+       */
+      if (mntent->mnt_fsname != NULL &&
+         mntent->mnt_fsname[0] == '/' &&
+         g_hash_table_lookup (mounts_hash, mntent->mnt_fsname))
+       continue;
+      
+      mount_entry = g_new0 (GUnixMount, 1);
+      mount_entry->mount_path = g_strdup (mntent->mnt_dir);
+      mount_entry->device_path = g_strdup (mntent->mnt_fsname);
+      mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
+      
+#if defined (HAVE_HASMNTOPT)
+      if (hasmntopt (mntent, MNTOPT_RO) != NULL)
+       mount_entry->is_read_only = TRUE;
+#endif
+      
+      mount_entry->is_system_internal =
+       guess_system_internal (mount_entry->mount_path,
+                              mount_entry->filesystem_type,
+                              mount_entry->device_path);
+      
+      g_hash_table_insert (mounts_hash,
+                          mount_entry->device_path,
+                          mount_entry->device_path);
+      
+      return_list = g_list_prepend (return_list, mount_entry);
+    }
+  g_hash_table_destroy (mounts_hash);
+  
+  endmntent (file);
+
+  G_UNLOCK (getmntent);
+  
+  return g_list_reverse (return_list);
+}
+
+#elif defined (HAVE_SYS_MNTTAB_H)
+
+G_LOCK_DEFINE_STATIC(getmntent);
+
+static char *
+get_mtab_read_file (void)
+{
+#ifdef _PATH_MOUNTED
+  return _PATH_MOUNTED;
+#else  
+  return "/etc/mnttab";
+#endif
+}
+
+static char *
+get_mtab_monitor_file (void)
+{
+  return get_mtab_read_file ();
+}
+
+static GList *
+_g_get_unix_mounts (void)
+{
+  struct mnttab mntent;
+  FILE *file;
+  char *read_file;
+  GUnixMount *mount_entry;
+  GList *return_list;
+  
+  read_file = get_mtab_read_file ();
+  
+  file = setmntent (read_file, "r");
+  if (file == NULL)
+    return NULL;
+  
+  return_list = NULL;
+  
+  G_LOCK (getmntent);
+  while (! getmntent (file, &mntent))
+    {
+      mount_entry = g_new0 (GUnixMount, 1);
+      
+      mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
+      mount_entry->device_path = g_strdup (mntent.mnt_special);
+      mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
+      
+#if defined (HAVE_HASMNTOPT)
+      if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
+       mount_entry->is_read_only = TRUE;
+#endif
+
+      mount_entry->is_system_internal =
+       guess_system_internal (mount_entry->mount_path,
+                              mount_entry->filesystem_type,
+                              mount_entry->device_path);
+      
+      return_list = g_list_prepend (return_list, mount_entry);
+    }
+  
+  endmntent (file);
+  
+  G_UNLOCK (getmntent);
+  
+  return g_list_reverse (return_list);
+}
+
+#elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
+
+static char *
+get_mtab_monitor_file (void)
+{
+  return NULL;
+}
+
+static GList *
+_g_get_unix_mounts (void)
+{
+  struct vfs_ent *fs_info;
+  struct vmount *vmount_info;
+  int vmount_number;
+  unsigned int vmount_size;
+  int current;
+  GList *return_list;
+  
+  if (mntctl (MCTL_QUERY, sizeof (vmount_size), &vmount_size) != 0)
+    {
+      g_warning ("Unable to know the number of mounted volumes\n");
+      
+      return NULL;
+    }
+
+  vmount_info = (struct vmount*)g_malloc (vmount_size);
+
+  vmount_number = mntctl (MCTL_QUERY, vmount_size, vmount_info);
+  
+  if (vmount_info->vmt_revision != VMT_REVISION)
+    g_warning ("Bad vmount structure revision number, want %d, got %d\n", VMT_REVISION, vmount_info->vmt_revision);
+
+  if (vmount_number < 0)
+    {
+      g_warning ("Unable to recover mounted volumes information\n");
+      
+      g_free (vmount_info);
+      return NULL;
+    }
+  
+  return_list = NULL;
+  while (vmount_number > 0)
+    {
+      mount_entry = g_new0 (GUnixMount, 1);
+      
+      mount_entry->device_path = g_strdup (vmt2dataptr (vmount_info, VMT_OBJECT));
+      mount_entry->mount_path = g_strdup (vmt2dataptr (vmount_info, VMT_STUB));
+      /* is_removable = (vmount_info->vmt_flags & MNT_REMOVABLE) ? 1 : 0; */
+      mount_entry->is_read_only = (vmount_info->vmt_flags & MNT_READONLY) ? 1 : 0;
+
+      fs_info = getvfsbytype (vmount_info->vmt_gfstype);
+      
+      if (fs_info == NULL)
+       mount_entry->filesystem_type = g_strdup ("unknown");
+      else
+       mount_entry->filesystem_type = g_strdup (fs_info->vfsent_name);
+
+      mount_entry->is_system_internal =
+       guess_system_internal (mount_entry->mount_path,
+                              mount_entry->filesystem_type,
+                              mount_entry->device_path);
+      
+      return_list = g_list_prepend (return_list, mount_entry);
+      
+      vmount_info = (struct vmount *)( (char*)vmount_info 
+                                      + vmount_info->vmt_length);
+      vmount_number--;
+    }
+  
+  
+  g_free (vmount_info);
+  
+  return g_list_reverse (return_list);
+}
+
+#elif defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
+
+static char *
+get_mtab_monitor_file (void)
+{
+  return NULL;
+}
+
+static GList *
+_g_get_unix_mounts (void)
+{
+  struct statfs *mntent = NULL;
+  int num_mounts, i;
+  GUnixMount *mount_entry;
+  GList *return_list;
+  
+  /* Pass MNT_NOWAIT to avoid blocking trying to update NFS mounts. */
+  if ((num_mounts = getmntinfo (&mntent, MNT_NOWAIT)) == 0)
+    return NULL;
+  
+  return_list = NULL;
+  
+  for (i = 0; i < num_mounts; i++)
+    {
+      mount_entry = g_new0 (GUnixMount, 1);
+      
+      mount_entry->mount_path = g_strdup (mntent[i].f_mntonname);
+      mount_entry->device_path = g_strdup (mntent[i].f_mntfromname);
+      mount_entry->filesystem_type = g_strdup (mntent[i].f_fstypename);
+      if (mntent[i].f_flags & MNT_RDONLY)
+       mount_entry->is_read_only = TRUE;
+
+      mount_entry->is_system_internal =
+       guess_system_internal (mount_entry->mount_path,
+                              mount_entry->filesystem_type,
+                              mount_entry->device_path);
+      
+      return_list = g_list_prepend (return_list, mount_entry);
+    }
+  
+  return g_list_reverse (return_list);
+}
+#else
+#error No _g_get_unix_mounts() implementation for system
+#endif
+
+/* _g_get_unix_mount_points():
+ * read the fstab.
+ * don't return swap and ignore mounts.
+ */
+
+static char *
+get_fstab_file (void)
+{
+#if defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
+  /* AIX */
+  return "/etc/filesystems";
+#elif defined(_PATH_MNTTAB)
+  return _PATH_MNTTAB;
+#elif defined(VFSTAB)
+  return VFSTAB;
+#else
+  return "/etc/fstab";
+#endif
+}
+
+#ifdef HAVE_MNTENT_H
+static GList *
+_g_get_unix_mount_points (void)
+{
+  struct mntent *mntent;
+  FILE *file;
+  char *read_file;
+  GUnixMountPoint *mount_entry;
+  GList *return_list;
+  
+  read_file = get_fstab_file ();
+  
+  file = setmntent (read_file, "r");
+  if (file == NULL)
+    return NULL;
+
+  return_list = NULL;
+  
+  G_LOCK (getmntent);
+  while ((mntent = getmntent (file)) != NULL)
+    {
+      if ((strcmp (mntent->mnt_dir, "ignore") == 0) ||
+         (strcmp (mntent->mnt_dir, "swap") == 0))
+       continue;
+      
+      mount_entry = g_new0 (GUnixMountPoint, 1);
+      mount_entry->mount_path = g_strdup (mntent->mnt_dir);
+      mount_entry->device_path = g_strdup (mntent->mnt_fsname);
+      mount_entry->filesystem_type = g_strdup (mntent->mnt_type);
+      
+#ifdef HAVE_HASMNTOPT
+      if (hasmntopt (mntent, MNTOPT_RO) != NULL)
+       mount_entry->is_read_only = TRUE;
+      
+      if (hasmntopt (mntent, "loop") != NULL)
+       mount_entry->is_loopback = TRUE;
+      
+#endif
+      
+      if ((mntent->mnt_type != NULL && strcmp ("supermount", mntent->mnt_type) == 0)
+#ifdef HAVE_HASMNTOPT
+         || (hasmntopt (mntent, "user") != NULL
+             && hasmntopt (mntent, "user") != hasmntopt (mntent, "user_xattr"))
+         || hasmntopt (mntent, "pamconsole") != NULL
+         || hasmntopt (mntent, "users") != NULL
+         || hasmntopt (mntent, "owner") != NULL
+#endif
+         )
+       mount_entry->is_user_mountable = TRUE;
+      
+      return_list = g_list_prepend (return_list, mount_entry);
+    }
+  
+  endmntent (file);
+  G_UNLOCK (getmntent);
+  
+  return g_list_reverse (return_list);
+}
+
+#elif defined (HAVE_SYS_MNTTAB_H)
+
+static GList *
+_g_get_unix_mount_points (void)
+{
+  struct mnttab mntent;
+  FILE *file;
+  char *read_file;
+  GUnixMountPoint *mount_entry;
+  GList *return_list;
+  
+  read_file = get_fstab_file ();
+  
+  file = setmntent (read_file, "r");
+  if (file == NULL)
+    return NULL;
+
+  return_list = NULL;
+  
+  G_LOCK (getmntent);
+  while (! getmntent (file, &mntent))
+    {
+      if ((strcmp (mntent.mnt_mountp, "ignore") == 0) ||
+         (strcmp (mntent.mnt_mountp, "swap") == 0))
+       continue;
+      
+      mount_entry = g_new0 (GUnixMountPoint, 1);
+      
+      mount_entry->mount_path = g_strdup (mntent.mnt_mountp);
+      mount_entry->device_path = g_strdup (mntent.mnt_special);
+      mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
+      
+#ifdef HAVE_HASMNTOPT
+      if (hasmntopt (&mntent, MNTOPT_RO) != NULL)
+       mount_entry->is_read_only = TRUE;
+      
+      if (hasmntopt (&mntent, "lofs") != NULL)
+       mount_entry->is_loopback = TRUE;
+#endif
+      
+      if ((mntent.mnt_fstype != NULL)
+#ifdef HAVE_HASMNTOPT
+         || (hasmntopt (&mntent, "user") != NULL
+             && hasmntopt (&mntent, "user") != hasmntopt (&mntent, "user_xattr"))
+         || hasmntopt (&mntent, "pamconsole") != NULL
+         || hasmntopt (&mntent, "users") != NULL
+         || hasmntopt (&mntent, "owner") != NULL
+#endif
+         )
+       mount_entry->is_user_mountable = TRUE;
+      
+      
+      return_list = g_list_prepend (return_list, mount_entry);
+    }
+  
+  endmntent (file);
+  G_UNLOCK (getmntent);
+  
+  return g_list_reverse (return_list);
+}
+#elif defined(HAVE_SYS_MNTCTL_H) && defined(HAVE_SYS_VMOUNT_H) && defined(HAVE_SYS_VFS_H)
+
+/* functions to parse /etc/filesystems on aix */
+
+/* read character, ignoring comments (begin with '*', end with '\n' */
+static int
+aix_fs_getc (FILE *fd)
+{
+  int c;
+  
+  while ((c = getc (fd)) == '*')
+    {
+      while (((c = getc (fd)) != '\n') && (c != EOF))
+       ;
+    }
+}
+
+/* eat all continuous spaces in a file */
+static int
+aix_fs_ignorespace (FILE *fd)
+{
+  int c;
+  
+  while ((c = aix_fs_getc (fd)) != EOF)
+    {
+      if (!g_ascii_isspace (c))
+       {
+         ungetc (c,fd);
+         return c;
+       }
+    }
+  
+  return EOF;
+}
+
+/* read one word from file */
+static int
+aix_fs_getword (FILE *fd, char *word)
+{
+  int c;
+  
+  aix_fs_ignorespace (fd);
+
+  while (((c = aix_fs_getc (fd)) != EOF) && !g_ascii_isspace (c))
+    {
+      if (c == '"')
+       {
+         while (((c = aix_fs_getc (fd)) != EOF) && (c != '"'))
+           *word++ = c;
+         else
+           *word++ = c;
+       }
+    }
+  *word = 0;
+  
+  return c;
+}
+
+typedef struct {
+  char mnt_mount[PATH_MAX];
+  char mnt_special[PATH_MAX];
+  char mnt_fstype[16];
+  char mnt_options[128];
+} AixMountTableEntry;
+
+/* read mount points properties */
+static int
+aix_fs_get (FILE *fd, AixMountTableEntry *prop)
+{
+  static char word[PATH_MAX] = { 0 };
+  char value[PATH_MAX];
+  
+  /* read stanza */
+  if (word[0] == 0)
+    {
+      if (aix_fs_getword (fd, word) == EOF)
+       return EOF;
+    }
+
+  word[strlen(word) - 1] = 0;
+  strcpy (prop->mnt_mount, word);
+  
+  /* read attributes and value */
+  
+  while (aix_fs_getword (fd, word) != EOF)
+    {
+      /* test if is attribute or new stanza */
+      if (word[strlen(word) - 1] == ':')
+       return 0;
+      
+      /* read "=" */
+      aix_fs_getword (fd, value);
+      
+      /* read value */
+      aix_fs_getword (fd, value);
+      
+      if (strcmp (word, "dev") == 0)
+       strcpy (prop->mnt_special, value);
+      else if (strcmp (word, "vfs") == 0)
+       strcpy (prop->mnt_fstype, value);
+      else if (strcmp (word, "options") == 0)
+       strcpy(prop->mnt_options, value);
+    }
+  
+  return 0;
+}
+
+static GList *
+_g_get_unix_mount_points (void)
+{
+  struct mntent *mntent;
+  FILE *file;
+  char *read_file;
+  GUnixMountPoint *mount_entry;
+  AixMountTableEntry mntent;
+  GList *return_list;
+  
+  read_file = get_fstab_file ();
+  
+  file = setmntent (read_file, "r");
+  if (file == NULL)
+    return NULL;
+  
+  return_list = NULL;
+  
+  while (!aix_fs_get (file, &mntent))
+    {
+      if (strcmp ("cdrfs", mntent.mnt_fstype) == 0)
+       {
+         mount_entry = g_new0 (GUnixMountPoint, 1);
+         
+         
+         mount_entry->mount_path = g_strdup (mntent.mnt_mount);
+         mount_entry->device_path = g_strdup (mntent.mnt_special);
+         mount_entry->filesystem_type = g_strdup (mntent.mnt_fstype);
+         mount_entry->is_read_only = TRUE;
+         mount_entry->is_user_mountable = TRUE;
+         
+         return_list = g_list_prepend (return_list, mount_entry);
+       }
+    }
+       
+  endmntent (file);
+  
+  return g_list_reverse (return_list);
+}
+
+#elif defined(HAVE_GETMNTINFO) && defined(HAVE_FSTAB_H) && defined(HAVE_SYS_MOUNT_H)
+
+static GList *
+_g_get_unix_mount_points (void)
+{
+  struct fstab *fstab = NULL;
+  GUnixMountPoint *mount_entry;
+  GList *return_list;
+#ifdef HAVE_SYS_SYSCTL_H
+  int usermnt = 0;
+  size_t len = sizeof(usermnt);
+  struct stat sb;
+#endif
+  
+  if (!setfsent ())
+    return NULL;
+
+  return_list = NULL;
+  
+#ifdef HAVE_SYS_SYSCTL_H
+#if defined(HAVE_SYSCTLBYNAME)
+  sysctlbyname ("vfs.usermount", &usermnt, &len, NULL, 0);
+#elif defined(CTL_VFS) && defined(VFS_USERMOUNT)
+  {
+    int mib[2];
+    
+    mib[0] = CTL_VFS;
+    mib[1] = VFS_USERMOUNT;
+    sysctl (mib, 2, &usermnt, &len, NULL, 0);
+  }
+#elif defined(CTL_KERN) && defined(KERN_USERMOUNT)
+  {
+    int mib[2];
+    
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_USERMOUNT;
+    sysctl (mib, 2, &usermnt, &len, NULL, 0);
+  }
+#endif
+#endif
+  
+  while ((fstab = getfsent ()) != NULL)
+    {
+      if (strcmp (fstab->fs_vfstype, "swap") == 0)
+       continue;
+      
+      mount_entry = g_new0 (GUnixMountPoint, 1);
+      
+      mount_entry->mount_path = g_strdup (fstab->fs_file);
+      mount_entry->device_path = g_strdup (fstab->fs_spec);
+      mount_entry->filesystem_type = g_strdup (fstab->fs_vfstype);
+      
+      if (strcmp (fstab->fs_type, "ro") == 0)
+       mount_entry->is_read_only = TRUE;
+
+#ifdef HAVE_SYS_SYSCTL_H
+      if (usermnt != 0)
+       {
+         uid_t uid = getuid ();
+         if (stat (fstab->fs_file, &sb) == 0)
+           {
+             if (uid == 0 || sb.st_uid == uid)
+               mount_entry->is_user_mountable = TRUE;
+           }
+       }
+#endif
+
+      return_list = g_list_prepend (return_list, mount_entry);
+    }
+  
+  endfsent ();
+  
+  return g_list_reverse (return_list);
+}
+#else
+#error No g_get_mount_table() implementation for system
+#endif
+
+static guint64
+get_mounts_timestamp (void)
+{
+  const char *monitor_file;
+  struct stat buf;
+
+  monitor_file = get_mtab_monitor_file ();
+  if (monitor_file)
+    {
+      if (stat (monitor_file, &buf) == 0)
+       return (guint64)buf.st_mtime;
+    }
+  return 0;
+}
+
+static guint64
+get_mount_points_timestamp (void)
+{
+  const char *monitor_file;
+  struct stat buf;
+
+  monitor_file = get_fstab_file ();
+  if (monitor_file)
+    {
+      if (stat (monitor_file, &buf) == 0)
+       return (guint64)buf.st_mtime;
+    }
+  return 0;
+}
+
+/**
+ * g_get_unix_mounts:
+ * @time_read: guint64 to contain a timestamp.
+ * 
+ * Returns a #GList of the UNIX mounts. If @time_read
+ * is set, it will be filled with the mount timestamp.
+ **/
+GList *
+g_get_unix_mounts (guint64 *time_read)
+{
+  if (time_read)
+    *time_read = get_mounts_timestamp ();
+
+  return _g_get_unix_mounts ();
+}
+
+/**
+ * g_get_unix_mount_at:
+ * @mount_path: path to mount.
+ * @time_read: guint64 to contain a timestamp.
+ * 
+ * Returns a #GUnixMount. If @time_read
+ * is set, it will be filled with the mount timestamp.
+ **/
+GUnixMount *
+g_get_unix_mount_at (const char *mount_path,
+                    guint64 *time_read)
+{
+  GList *mounts, *l;
+  GUnixMount *mount_entry, *found;
+  
+  mounts = g_get_unix_mounts (time_read);
+
+  found = NULL;
+  for (l = mounts; l != NULL; l = l->next)
+    {
+      mount_entry = l->data;
+
+      if (strcmp (mount_path, mount_entry->mount_path) == 0)
+       found = mount_entry;
+      else
+       g_unix_mount_free (mount_entry);
+      
+    }
+  g_list_free (mounts);
+
+  return found;
+}
+
+/**
+ * g_get_unix_mount_points:
+ * @time_read: guint64 to contain a timestamp.
+ * 
+ * Returns a #GList of the UNIX mountpoints. If @time_read
+ * is set, it will be filled with the mount timestamp.
+ **/
+GList *
+g_get_unix_mount_points (guint64 *time_read)
+{
+  if (time_read)
+    *time_read = get_mount_points_timestamp ();
+
+  return _g_get_unix_mount_points ();
+}
+
+/**
+ * g_unix_mounts_change_since:
+ * @time: guint64 to contain a timestamp.
+ * 
+ * Returns %TRUE if the mounts have changed since @time. 
+ **/
+gboolean
+g_unix_mounts_changed_since (guint64 time)
+{
+  return get_mounts_timestamp () != time;
+}
+
+/**
+ * g_unix_mount_points_change_since:
+ * @time: guint64 to contain a timestamp.
+ * 
+ * Returns %TRUE if the mount points have changed since @time. 
+ **/
+gboolean
+g_unix_mount_points_changed_since (guint64 time)
+{
+  return get_mount_points_timestamp () != time;
+}
+
+static void
+g_unix_mount_monitor_finalize (GObject *object)
+{
+  GUnixMountMonitor *monitor;
+  
+  monitor = G_UNIX_MOUNT_MONITOR (object);
+
+  if (monitor->fstab_monitor)
+    {
+      g_file_monitor_cancel (monitor->fstab_monitor);
+      g_object_unref (monitor->fstab_monitor);
+    }
+  
+  if (monitor->mtab_monitor)
+    {
+      g_file_monitor_cancel (monitor->mtab_monitor);
+      g_object_unref (monitor->mtab_monitor);
+    }
+
+  the_mount_monitor = NULL;
+  
+  if (G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_unix_mount_monitor_parent_class)->finalize) (object);
+}
+
+
+static void
+g_unix_mount_monitor_class_init (GUnixMountMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_unix_mount_monitor_finalize;
+  
+  signals[MOUNTS_CHANGED] =
+    g_signal_new ("mounts_changed",
+                 G_TYPE_FROM_CLASS (klass),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+  
+  signals[MOUNTPOINTS_CHANGED] =
+    g_signal_new ("mountpoints_changed",
+                 G_TYPE_FROM_CLASS (klass),
+                 G_SIGNAL_RUN_LAST,
+                 0,
+                 NULL, NULL,
+                 g_cclosure_marshal_VOID__VOID,
+                 G_TYPE_NONE, 0);
+}
+
+static void
+fstab_file_changed (GFileMonitor* monitor,
+                   GFile* file,
+                   GFile* other_file,
+                   GFileMonitorEvent event_type,
+                   gpointer user_data)
+{
+  GUnixMountMonitor *mount_monitor;
+
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+      event_type != G_FILE_MONITOR_EVENT_CREATED &&
+      event_type != G_FILE_MONITOR_EVENT_DELETED)
+    return;
+
+  mount_monitor = user_data;
+  g_signal_emit (mount_monitor, signals[MOUNTPOINTS_CHANGED], 0);
+}
+
+static void
+mtab_file_changed (GFileMonitor* monitor,
+                  GFile* file,
+                  GFile* other_file,
+                  GFileMonitorEvent event_type,
+                  gpointer user_data)
+{
+  GUnixMountMonitor *mount_monitor;
+
+  if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+      event_type != G_FILE_MONITOR_EVENT_CREATED &&
+      event_type != G_FILE_MONITOR_EVENT_DELETED)
+    return;
+  
+  mount_monitor = user_data;
+  g_signal_emit (mount_monitor, signals[MOUNTS_CHANGED], 0);
+}
+
+static void
+g_unix_mount_monitor_init (GUnixMountMonitor *monitor)
+{
+  GFile *file;
+    
+  if (get_fstab_file () != NULL)
+    {
+      file = g_file_new_for_path (get_fstab_file ());
+      monitor->fstab_monitor = g_file_monitor_file (file, 0, NULL);
+      g_object_unref (file);
+      
+      g_signal_connect (monitor->fstab_monitor, "changed", (GCallback)fstab_file_changed, monitor);
+    }
+  
+  if (get_mtab_monitor_file () != NULL)
+    {
+      file = g_file_new_for_path (get_mtab_monitor_file ());
+      monitor->mtab_monitor = g_file_monitor_file (file, 0, NULL);
+      g_object_unref (file);
+      
+      g_signal_connect (monitor->mtab_monitor, "changed", (GCallback)mtab_file_changed, monitor);
+    }
+}
+
+/**
+ * g_unix_mount_monitor_new:
+ * 
+ * Returns a new #GUnixMountMonitor. 
+ **/
+GUnixMountMonitor *
+g_unix_mount_monitor_new (void)
+{
+  if (the_mount_monitor == NULL)
+    {
+      the_mount_monitor = g_object_new (G_TYPE_UNIX_MOUNT_MONITOR, NULL);
+      return the_mount_monitor;
+    }
+  
+  return g_object_ref (the_mount_monitor);
+}
+
+/**
+ * g_unix_mount_free:
+ * @mount_entry: a #GUnixMount.
+ * 
+ **/
+void
+g_unix_mount_free (GUnixMount *mount_entry)
+{
+  g_return_if_fail (mount_entry != NULL);
+
+  g_free (mount_entry->mount_path);
+  g_free (mount_entry->device_path);
+  g_free (mount_entry->filesystem_type);
+  g_free (mount_entry);
+}
+
+/**
+ * g_unix_mount_point_free:
+ * @mount_point: 
+ * 
+ **/
+void
+g_unix_mount_point_free (GUnixMountPoint *mount_point)
+{
+  g_return_if_fail (mount_point != NULL);
+
+  g_free (mount_point->mount_path);
+  g_free (mount_point->device_path);
+  g_free (mount_point->filesystem_type);
+  g_free (mount_point);
+}
+
+static int
+strcmp_null (const char *str1,
+            const char *str2)
+{
+  if (str1 == str2)
+    return 0;
+  if (str1 == NULL && str2 != NULL) 
+    return -1;
+  if (str1 != NULL && str2 == NULL)
+    return 1;
+  return strcmp (str1, str2);
+}
+
+/**
+ * g_unix_mount_compare:
+ * @mount1: first #GUnixMount to compare.
+ * @mount2: second #GUnixMount to compare.
+ * 
+ * Returns 1, 0 or -1 if @mount1 is greater than, equal to,
+ * or less than @mount2, respectively. 
+ **/
+gint
+g_unix_mount_compare (GUnixMount      *mount1,
+                     GUnixMount      *mount2)
+{
+  int res;
+
+  g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
+  
+  res = strcmp_null (mount1->mount_path, mount2->mount_path);
+  if (res != 0)
+    return res;
+       
+  res = strcmp_null (mount1->device_path, mount2->device_path);
+  if (res != 0)
+    return res;
+       
+  res = strcmp_null (mount1->filesystem_type, mount2->filesystem_type);
+  if (res != 0)
+    return res;
+
+  res =  mount1->is_read_only - mount2->is_read_only;
+  if (res != 0)
+    return res;
+  
+  return 0;
+}
+
+/**
+ * g_unix_mount_get_mount_path:
+ * @mount_entry: input #GUnixMount to get the mount path for.
+ * 
+ * Returns the mount path for @mount_entry.
+ * 
+ **/
+const char *
+g_unix_mount_get_mount_path (GUnixMount *mount_entry)
+{
+  g_return_val_if_fail (mount_entry != NULL, NULL);
+
+  return mount_entry->mount_path;
+}
+
+/**
+ * g_unix_mount_get_device_path:
+ * @mount_entry: a #GUnixMount.
+ * 
+ **/
+const char *
+g_unix_mount_get_device_path (GUnixMount *mount_entry)
+{
+  g_return_val_if_fail (mount_entry != NULL, NULL);
+
+  return mount_entry->device_path;
+}
+
+/**
+ * g_unix_mount_get_fs_type:
+ * @mount_entry: a #GUnixMount.
+ * 
+ **/
+const char *
+g_unix_mount_get_fs_type (GUnixMount *mount_entry)
+{
+  g_return_val_if_fail (mount_entry != NULL, NULL);
+
+  return mount_entry->filesystem_type;
+}
+
+/**
+ * g_unix_mount_is_readonly:
+ * @mount_entry: a #GUnixMount.
+ * 
+ * Returns %TRUE if @mount_entry is read only.
+ * 
+ **/
+gboolean
+g_unix_mount_is_readonly (GUnixMount *mount_entry)
+{
+  g_return_val_if_fail (mount_entry != NULL, FALSE);
+
+  return mount_entry->is_read_only;
+}
+
+/**
+ * g_unix_mount_is_system_internal:
+ * @mount_entry: a #GUnixMount.
+ * 
+ **/
+gboolean
+g_unix_mount_is_system_internal (GUnixMount *mount_entry)
+{
+  g_return_val_if_fail (mount_entry != NULL, FALSE);
+
+  return mount_entry->is_system_internal;
+}
+
+/**
+ * g_unix_mount_point_compare:
+ * @mount1: a #GUnixMount.
+ * @mount2: a #GUnixMount.
+ * 
+ * Returns 1, 0 or -1 if @mount1 is greater than, equal to,
+ * or less than @mount2, respectively.
+ **/
+gint
+g_unix_mount_point_compare (GUnixMountPoint *mount1,
+                           GUnixMountPoint *mount2)
+{
+  int res;
+
+  g_return_val_if_fail (mount1 != NULL && mount2 != NULL, 0);
+
+  res = strcmp_null (mount1->mount_path, mount2->mount_path);
+  if (res != 0) 
+    return res;
+       
+  res = strcmp_null (mount1->device_path, mount2->device_path);
+  if (res != 0) 
+    return res;
+       
+  res = strcmp_null (mount1->filesystem_type, mount2->filesystem_type);
+  if (res != 0) 
+    return res;
+
+  res =  mount1->is_read_only - mount2->is_read_only;
+  if (res != 0) 
+    return res;
+
+  res = mount1->is_user_mountable - mount2->is_user_mountable;
+  if (res != 0) 
+    return res;
+
+  res = mount1->is_loopback - mount2->is_loopback;
+  if (res != 0)
+    return res;
+  
+  return 0;
+}
+
+/**
+ * g_unix_mount_point_get_mount_path:
+ * @mount_point: a #GUnixMount.
+ * 
+ **/
+const char *
+g_unix_mount_point_get_mount_path (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, NULL);
+
+  return mount_point->mount_path;
+}
+
+/**
+ * g_unix_mount_point_get_device_path:
+ * @mount_point: a #GUnixMount.
+ * 
+ **/
+const char *
+g_unix_mount_point_get_device_path (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, NULL);
+
+  return mount_point->device_path;
+}
+
+/**
+ * g_unix_mount_point_get_fs_type:
+ * @mount_point: a #GUnixMount.
+ * 
+ **/
+const char *
+g_unix_mount_point_get_fs_type (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, NULL);
+
+  return mount_point->filesystem_type;
+}
+
+/**
+ * g_unix_mount_point_is_readonly:
+ * @mount_point: a #GUnixMount.
+ * 
+ **/
+gboolean
+g_unix_mount_point_is_readonly (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, FALSE);
+
+  return mount_point->is_read_only;
+}
+
+/**
+ * g_unix_mount_point_is_user_mountable:
+ * @mount_point:  a #GUnixMount.
+ * 
+ **/
+gboolean
+g_unix_mount_point_is_user_mountable (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, FALSE);
+
+  return mount_point->is_user_mountable;
+}
+
+/**
+ * g_unix_mount_point_is_loopback:
+ * @mount_point:  a #GUnixMount.
+ * 
+ **/
+gboolean
+g_unix_mount_point_is_loopback (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, FALSE);
+
+  return mount_point->is_loopback;
+}
+
+static GUnixMountType
+guess_mount_type (const char *mount_path,
+                 const char *device_path,
+                 const char *filesystem_type)
+{
+  GUnixMountType type;
+  char *basename;
+
+  type = G_UNIX_MOUNT_TYPE_UNKNOWN;
+  
+  if ((strcmp (filesystem_type, "udf") == 0) ||
+      (strcmp (filesystem_type, "iso9660") == 0) ||
+      (strcmp (filesystem_type, "cd9660") == 0))
+    type = G_UNIX_MOUNT_TYPE_CDROM;
+  else if (strcmp (filesystem_type, "nfs") == 0)
+    type = G_UNIX_MOUNT_TYPE_NFS;
+  else if (g_str_has_prefix (device_path, "/vol/dev/diskette/") ||
+          g_str_has_prefix (device_path, "/dev/fd") ||
+          g_str_has_prefix (device_path, "/dev/floppy"))
+    type = G_UNIX_MOUNT_TYPE_FLOPPY;
+  else if (g_str_has_prefix (device_path, "/dev/cdrom") ||
+          g_str_has_prefix (device_path, "/dev/acd") ||
+          g_str_has_prefix (device_path, "/dev/cd"))
+    type = G_UNIX_MOUNT_TYPE_CDROM;
+  else if (g_str_has_prefix (device_path, "/vol/"))
+    {
+      const char *name = mount_path + strlen ("/");
+      
+      if (g_str_has_prefix (name, "cdrom"))
+       type = G_UNIX_MOUNT_TYPE_CDROM;
+      else if (g_str_has_prefix (name, "floppy") ||
+              g_str_has_prefix (device_path, "/vol/dev/diskette/")) 
+       type = G_UNIX_MOUNT_TYPE_FLOPPY;
+      else if (g_str_has_prefix (name, "rmdisk")) 
+       type = G_UNIX_MOUNT_TYPE_ZIP;
+      else if (g_str_has_prefix (name, "jaz"))
+       type = G_UNIX_MOUNT_TYPE_JAZ;
+      else if (g_str_has_prefix (name, "memstick"))
+       type = G_UNIX_MOUNT_TYPE_MEMSTICK;
+    }
+  else
+    {
+      basename = g_path_get_basename (mount_path);
+      
+      if (g_str_has_prefix (basename, "cdrom") ||
+         g_str_has_prefix (basename, "cdwriter") ||
+         g_str_has_prefix (basename, "burn") ||
+         g_str_has_prefix (basename, "cdr") ||
+         g_str_has_prefix (basename, "cdrw") ||
+         g_str_has_prefix (basename, "dvdrom") ||
+         g_str_has_prefix (basename, "dvdram") ||
+         g_str_has_prefix (basename, "dvdr") ||
+         g_str_has_prefix (basename, "dvdrw") ||
+         g_str_has_prefix (basename, "cdrom_dvdrom") ||
+         g_str_has_prefix (basename, "cdrom_dvdram") ||
+         g_str_has_prefix (basename, "cdrom_dvdr") ||
+         g_str_has_prefix (basename, "cdrom_dvdrw") ||
+         g_str_has_prefix (basename, "cdr_dvdrom") ||
+         g_str_has_prefix (basename, "cdr_dvdram") ||
+         g_str_has_prefix (basename, "cdr_dvdr") ||
+         g_str_has_prefix (basename, "cdr_dvdrw") ||
+         g_str_has_prefix (basename, "cdrw_dvdrom") ||
+         g_str_has_prefix (basename, "cdrw_dvdram") ||
+         g_str_has_prefix (basename, "cdrw_dvdr") ||
+         g_str_has_prefix (basename, "cdrw_dvdrw"))
+       type = G_UNIX_MOUNT_TYPE_CDROM;
+      else if (g_str_has_prefix (basename, "floppy"))
+       type = G_UNIX_MOUNT_TYPE_FLOPPY;
+      else if (g_str_has_prefix (basename, "zip"))
+       type = G_UNIX_MOUNT_TYPE_ZIP;
+      else if (g_str_has_prefix (basename, "jaz"))
+       type = G_UNIX_MOUNT_TYPE_JAZ;
+      else if (g_str_has_prefix (basename, "camera"))
+       type = G_UNIX_MOUNT_TYPE_CAMERA;
+      else if (g_str_has_prefix (basename, "memstick") ||
+              g_str_has_prefix (basename, "memory_stick") ||
+              g_str_has_prefix (basename, "ram"))
+       type = G_UNIX_MOUNT_TYPE_MEMSTICK;
+      else if (g_str_has_prefix (basename, "compact_flash"))
+       type = G_UNIX_MOUNT_TYPE_CF;
+      else if (g_str_has_prefix (basename, "smart_media"))
+       type = G_UNIX_MOUNT_TYPE_SM;
+      else if (g_str_has_prefix (basename, "sd_mmc"))
+       type = G_UNIX_MOUNT_TYPE_SDMMC;
+      else if (g_str_has_prefix (basename, "ipod"))
+       type = G_UNIX_MOUNT_TYPE_IPOD;
+      
+      g_free (basename);
+    }
+  
+  if (type == G_UNIX_MOUNT_TYPE_UNKNOWN)
+    type = G_UNIX_MOUNT_TYPE_HD;
+  
+  return type;
+}
+
+/**
+ * g_unix_mount_guess_type:
+ * @mount_entry: a #GUnixMount.
+ * 
+ **/
+GUnixMountType
+g_unix_mount_guess_type (GUnixMount *mount_entry)
+{
+  g_return_val_if_fail (mount_entry != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+  g_return_val_if_fail (mount_entry->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+  g_return_val_if_fail (mount_entry->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+  g_return_val_if_fail (mount_entry->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+
+  return guess_mount_type (mount_entry->mount_path,
+                          mount_entry->device_path,
+                          mount_entry->filesystem_type);
+}
+
+/**
+ * g_unix_mount_point_guess_type:
+ * @mount_point: a #GUnixMount.
+ * 
+ **/
+GUnixMountType
+g_unix_mount_point_guess_type (GUnixMountPoint *mount_point)
+{
+  g_return_val_if_fail (mount_point != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+  g_return_val_if_fail (mount_point->mount_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+  g_return_val_if_fail (mount_point->device_path != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+  g_return_val_if_fail (mount_point->filesystem_type != NULL, G_UNIX_MOUNT_TYPE_UNKNOWN);
+
+  return guess_mount_type (mount_point->mount_path,
+                          mount_point->device_path,
+                          mount_point->filesystem_type);
+}
diff --git a/gio/gunixmounts.h b/gio/gunixmounts.h
new file mode 100644 (file)
index 0000000..7e9c03b
--- /dev/null
@@ -0,0 +1,92 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_UNIX_MOUNTS_H__
+#define __G_UNIX_MOUNTS_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GUnixMount GUnixMount;
+typedef struct _GUnixMountPoint GUnixMountPoint;
+
+typedef enum {
+  G_UNIX_MOUNT_TYPE_UNKNOWN,
+  G_UNIX_MOUNT_TYPE_FLOPPY,
+  G_UNIX_MOUNT_TYPE_CDROM,
+  G_UNIX_MOUNT_TYPE_NFS,
+  G_UNIX_MOUNT_TYPE_ZIP,
+  G_UNIX_MOUNT_TYPE_JAZ,
+  G_UNIX_MOUNT_TYPE_MEMSTICK,
+  G_UNIX_MOUNT_TYPE_CF,
+  G_UNIX_MOUNT_TYPE_SM,
+  G_UNIX_MOUNT_TYPE_SDMMC,
+  G_UNIX_MOUNT_TYPE_IPOD,
+  G_UNIX_MOUNT_TYPE_CAMERA,
+  G_UNIX_MOUNT_TYPE_HD
+} GUnixMountType;
+
+typedef struct _GUnixMountMonitor GUnixMountMonitor;
+typedef struct _GUnixMountMonitorClass GUnixMountMonitorClass;
+
+#define G_TYPE_UNIX_MOUNT_MONITOR        (g_unix_mount_monitor_get_type ())
+#define G_UNIX_MOUNT_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_MOUNT_MONITOR, GUnixMountMonitor))
+#define G_UNIX_MOUNT_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNIX_MOUNT_MONITOR, GUnixMountMonitorClass))
+#define G_IS_UNIX_MOUNT_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_MOUNT_MONITOR))
+#define G_IS_UNIX_MOUNT_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNIX_MOUNT_MONITOR))
+
+void           g_unix_mount_free                    (GUnixMount         *mount_entry);
+void           g_unix_mount_point_free              (GUnixMountPoint    *mount_point);
+gint           g_unix_mount_compare                 (GUnixMount         *mount1,
+                                                    GUnixMount         *mount2);
+const char *   g_unix_mount_get_mount_path          (GUnixMount         *mount_entry);
+const char *   g_unix_mount_get_device_path         (GUnixMount         *mount_entry);
+const char *   g_unix_mount_get_fs_type             (GUnixMount         *mount_entry);
+gboolean       g_unix_mount_is_readonly             (GUnixMount         *mount_entry);
+gboolean       g_unix_mount_is_system_internal      (GUnixMount         *mount_entry);
+GUnixMountType g_unix_mount_guess_type              (GUnixMount         *mount_entry);
+
+gint           g_unix_mount_point_compare           (GUnixMountPoint    *mount1,
+                                                    GUnixMountPoint    *mount2);
+const char *   g_unix_mount_point_get_mount_path    (GUnixMountPoint    *mount_point);
+const char *   g_unix_mount_point_get_device_path   (GUnixMountPoint    *mount_point);
+const char *   g_unix_mount_point_get_fs_type       (GUnixMountPoint    *mount_point);
+gboolean       g_unix_mount_point_is_readonly       (GUnixMountPoint    *mount_point);
+gboolean       g_unix_mount_point_is_user_mountable (GUnixMountPoint    *mount_point);
+gboolean       g_unix_mount_point_is_loopback       (GUnixMountPoint    *mount_point);
+GUnixMountType g_unix_mount_point_guess_type        (GUnixMountPoint    *mount_point);
+
+GList *        g_get_unix_mount_points              (guint64            *time_read);
+GList *        g_get_unix_mounts                    (guint64            *time_read);
+GUnixMount *   g_get_unix_mount_at                  (const char         *mount_path,
+                                                    guint64            *time_read);
+gboolean       g_unix_mounts_changed_since          (guint64             time);
+gboolean       g_unix_mount_points_changed_since    (guint64             time);
+
+GType              g_unix_mount_monitor_get_type (void) G_GNUC_CONST;
+GUnixMountMonitor *g_unix_mount_monitor_new      (void);
+
+G_END_DECLS
+
+#endif /* __G_UNIX_MOUNTS_H__ */
diff --git a/gio/gunixvolume.c b/gio/gunixvolume.c
new file mode 100644 (file)
index 0000000..6ce1260
--- /dev/null
@@ -0,0 +1,332 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include "gunixvolumemonitor.h"
+#include "gunixvolume.h"
+#include "gunixdrive.h"
+#include "gvolumeprivate.h"
+#include "gvolumemonitor.h"
+#include "gthemedicon.h"
+
+#include "glibintl.h"
+
+struct _GUnixVolume {
+  GObject parent;
+
+  GUnixDrive *drive; /* owned by volume monitor */
+  char *name;
+  char *icon;
+  char *mountpoint;
+};
+
+static void g_unix_volume_volume_iface_init (GVolumeIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GUnixVolume, g_unix_volume, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_VOLUME,
+                                               g_unix_volume_volume_iface_init))
+
+
+static void
+g_unix_volume_finalize (GObject *object)
+{
+  GUnixVolume *volume;
+  
+  volume = G_UNIX_VOLUME (object);
+
+  if (volume->drive)
+    g_unix_drive_unset_volume (volume->drive, volume);
+    
+  g_assert (volume->drive == NULL);
+  g_free (volume->name);
+  g_free (volume->icon);
+  g_free (volume->mountpoint);
+  
+  if (G_OBJECT_CLASS (g_unix_volume_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_unix_volume_parent_class)->finalize) (object);
+}
+
+static void
+g_unix_volume_class_init (GUnixVolumeClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = g_unix_volume_finalize;
+}
+
+static void
+g_unix_volume_init (GUnixVolume *unix_volume)
+{
+}
+
+static char *
+get_filesystem_volume_name (const char *fs_type)
+{
+  /* TODO: add translation table from gnome-vfs */
+  return g_strdup_printf (_("%s volume"), fs_type);
+}
+
+static char *
+type_to_icon (GUnixMountType type)
+{
+  const char *icon_name = NULL;
+  
+  switch (type)
+    {
+    case G_UNIX_MOUNT_TYPE_HD:
+      icon_name = "drive-harddisk";
+      break;
+    case G_UNIX_MOUNT_TYPE_FLOPPY:
+    case G_UNIX_MOUNT_TYPE_ZIP:
+    case G_UNIX_MOUNT_TYPE_JAZ:
+      icon_name = "media-floppy";
+      break;
+    case G_UNIX_MOUNT_TYPE_CDROM:
+      icon_name = "media-optical";
+      break;
+    case G_UNIX_MOUNT_TYPE_NFS:
+      /* TODO: Would like a better icon here... */
+      icon_name = "drive-harddisk";
+      break;
+    case G_UNIX_MOUNT_TYPE_MEMSTICK:
+      icon_name = "media-flash";
+      break;
+    case G_UNIX_MOUNT_TYPE_CAMERA:
+      icon_name = "camera-photo";
+      break;
+    case G_UNIX_MOUNT_TYPE_IPOD:
+      icon_name = "multimedia-player";
+      break;
+    case G_UNIX_MOUNT_TYPE_UNKNOWN:
+    default:
+      icon_name = "drive-harddisk";
+      break;
+    }
+  return g_strdup (icon_name);
+}
+
+/**
+ * g_unix_volume_new:
+ * @mount: 
+ * @drive:
+ * 
+ * Returns: a #GUnixVolume.
+ * 
+ **/
+GUnixVolume *
+g_unix_volume_new (GUnixMount *mount,
+                  GUnixDrive *drive)
+{
+  GUnixVolume *volume;
+  GUnixMountType type;
+  const char *mount_path;
+  char *volume_name;
+  
+  mount_path = g_unix_mount_get_mount_path (mount);
+  
+  /* No drive for volume. Ignore internal things */
+  if (drive == NULL && g_unix_mount_is_system_internal (mount))
+    return NULL;
+  
+  volume = g_object_new (G_TYPE_UNIX_VOLUME, NULL);
+  volume->drive = drive;
+  if (drive)
+    g_unix_drive_set_volume (drive, volume);
+  volume->mountpoint = g_strdup (mount_path);
+
+  type = g_unix_mount_guess_type (mount);
+  
+  volume->icon = type_to_icon (type);
+                                         
+  volume_name = NULL;
+  if (mount_path)
+    {
+      if (strcmp (mount_path, "/") == 0)
+       volume_name = g_strdup (_("Filesystem root"));
+      else
+       volume_name = g_filename_display_basename (mount_path);
+    }
+      
+  if (volume_name == NULL)
+    {
+      if (g_unix_mount_get_fs_type (mount) != NULL)
+       volume_name = g_strdup (get_filesystem_volume_name (g_unix_mount_get_fs_type (mount)));
+    }
+  
+  if (volume_name == NULL)
+    {
+      /* TODO: Use volume size as name? */
+      volume_name = g_strdup (_("Unknown volume"));
+    }
+  
+  volume->name = volume_name;
+
+  return volume;
+}
+
+/**
+ * g_unix_volume_unmounted:
+ * @volume: 
+ * 
+ **/
+void
+g_unix_volume_unmounted (GUnixVolume *volume)
+{
+  if (volume->drive)
+    {
+      g_unix_drive_unset_volume (volume->drive, volume);
+      volume->drive = NULL;
+      g_signal_emit_by_name (volume, "changed");
+    }
+}
+
+/**
+ * g_unix_volume_unset_drive:
+ * @volume:
+ * @drive:
+ *   
+ **/
+void
+g_unix_volume_unset_drive (GUnixVolume   *volume,
+                          GUnixDrive    *drive)
+{
+  if (volume->drive == drive)
+    {
+      volume->drive = NULL;
+      /* TODO: Emit changed in idle to avoid locking issues */
+      g_signal_emit_by_name (volume, "changed");
+    }
+}
+
+static GFile *
+g_unix_volume_get_root (GVolume *volume)
+{
+  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
+
+  return g_file_new_for_path (unix_volume->mountpoint);
+}
+
+static GIcon *
+g_unix_volume_get_icon (GVolume *volume)
+{
+  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
+
+  return g_themed_icon_new (unix_volume->icon);
+}
+
+static char *
+g_unix_volume_get_name (GVolume *volume)
+{
+  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
+  
+  return g_strdup (unix_volume->name);
+}
+
+/**
+ * g_unix_volume_has_mountpoint:
+ * @volume:
+ * @mountpoint:
+ * 
+ * Returns: 
+ **/
+gboolean
+g_unix_volume_has_mountpoint (GUnixVolume *volume,
+                             const char  *mountpoint)
+{
+  return strcmp (volume->mountpoint, mountpoint) == 0;
+}
+
+static GDrive *
+g_unix_volume_get_drive (GVolume *volume)
+{
+  GUnixVolume *unix_volume = G_UNIX_VOLUME (volume);
+
+  if (unix_volume->drive)
+    return G_DRIVE (g_object_ref (unix_volume->drive));
+  
+  return NULL;
+}
+
+static gboolean
+g_unix_volume_can_unmount (GVolume *volume)
+{
+  /* TODO */
+  return FALSE;
+}
+
+static gboolean
+g_unix_volume_can_eject (GVolume *volume)
+{
+  /* TODO */
+  return FALSE;
+}
+
+static void
+g_unix_volume_unmount (GVolume         *volume,
+                      GAsyncReadyCallback callback,
+                      gpointer         user_data)
+{
+  /* TODO */
+}
+
+static gboolean
+g_unix_volume_unmount_finish (GVolume *volume,
+                             GAsyncResult *result,
+                             GError **error)
+{
+  return TRUE;
+}
+
+static void
+g_unix_volume_eject (GVolume         *volume,
+                    GAsyncReadyCallback callback,
+                    gpointer         user_data)
+{
+  /* TODO */
+}
+
+static gboolean
+g_unix_volume_eject_finish (GVolume *volume,
+                           GAsyncResult *result,
+                           GError **error)
+{
+  return TRUE;
+}
+
+static void
+g_unix_volume_volume_iface_init (GVolumeIface *iface)
+{
+  iface->get_root = g_unix_volume_get_root;
+  iface->get_name = g_unix_volume_get_name;
+  iface->get_icon = g_unix_volume_get_icon;
+  iface->get_drive = g_unix_volume_get_drive;
+  iface->can_unmount = g_unix_volume_can_unmount;
+  iface->can_eject = g_unix_volume_can_eject;
+  iface->unmount = g_unix_volume_unmount;
+  iface->unmount_finish = g_unix_volume_unmount_finish;
+  iface->eject = g_unix_volume_eject;
+  iface->eject_finish = g_unix_volume_eject_finish;
+}
diff --git a/gio/gunixvolume.h b/gio/gunixvolume.h
new file mode 100644 (file)
index 0000000..b0cb406
--- /dev/null
@@ -0,0 +1,57 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_UNIX_VOLUME_H__
+#define __G_UNIX_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gvolume.h>
+#include <gio/gunixmounts.h>
+#include <gio/gunixvolumemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_UNIX_VOLUME        (g_unix_volume_get_type ())
+#define G_UNIX_VOLUME(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_VOLUME, GUnixVolume))
+#define G_UNIX_VOLUME_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNIX_VOLUME, GUnixVolumeClass))
+#define G_IS_UNIX_VOLUME(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_VOLUME))
+#define G_IS_UNIX_VOLUME_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNIX_VOLUME))
+
+typedef struct _GUnixVolumeClass GUnixVolumeClass;
+
+struct _GUnixVolumeClass {
+   GObjectClass parent_class;
+};
+
+GType g_unix_volume_get_type (void) G_GNUC_CONST;
+
+GUnixVolume *g_unix_volume_new            (GUnixMount     *mount,
+                                          GUnixDrive     *drive);
+gboolean     g_unix_volume_has_mountpoint (GUnixVolume    *volume,
+                                          const char     *mountpoint);
+void         g_unix_volume_unset_drive    (GUnixVolume    *volume,
+                                          GUnixDrive     *drive);
+void         g_unix_volume_unmounted      (GUnixVolume    *volume);
+
+G_END_DECLS
+
+#endif /* __G_UNIX_VOLUME_H__ */
diff --git a/gio/gunixvolumemonitor.c b/gio/gunixvolumemonitor.c
new file mode 100644 (file)
index 0000000..2430e4b
--- /dev/null
@@ -0,0 +1,382 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include <glib.h>
+#include "gunixvolumemonitor.h"
+#include "gunixmounts.h"
+#include "gunixvolume.h"
+#include "gunixdrive.h"
+#include "gvolumeprivate.h"
+
+#include "glibintl.h"
+
+struct _GUnixVolumeMonitor {
+  GNativeVolumeMonitor parent;
+
+  GUnixMountMonitor *mount_monitor;
+
+  GList *last_mountpoints;
+  GList *last_mounts;
+
+  GList *drives;
+  GList *volumes;
+};
+
+static void mountpoints_changed (GUnixMountMonitor  *mount_monitor,
+                                gpointer            user_data);
+static void mounts_changed      (GUnixMountMonitor  *mount_monitor,
+                                gpointer            user_data);
+static void update_drives       (GUnixVolumeMonitor *monitor);
+static void update_volumes      (GUnixVolumeMonitor *monitor);
+
+G_DEFINE_TYPE (GUnixVolumeMonitor, g_unix_volume_monitor, G_TYPE_NATIVE_VOLUME_MONITOR);
+
+static void
+g_unix_volume_monitor_finalize (GObject *object)
+{
+  GUnixVolumeMonitor *monitor;
+  
+  monitor = G_UNIX_VOLUME_MONITOR (object);
+
+  g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mountpoints_changed, monitor);
+  g_signal_handlers_disconnect_by_func (monitor->mount_monitor, mounts_changed, monitor);
+                                       
+  g_object_unref (monitor->mount_monitor);
+
+  g_list_foreach (monitor->last_mounts, (GFunc)g_unix_mount_free, NULL);
+  g_list_free (monitor->last_mounts);
+
+  g_list_foreach (monitor->volumes, (GFunc)g_object_unref, NULL);
+  g_list_free (monitor->volumes);
+  g_list_foreach (monitor->drives, (GFunc)g_object_unref, NULL);
+  g_list_free (monitor->drives);
+  
+  if (G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_unix_volume_monitor_parent_class)->finalize) (object);
+}
+
+static GList *
+get_mounted_volumes (GVolumeMonitor *volume_monitor)
+{
+  GUnixVolumeMonitor *monitor;
+  GList *l;
+  
+  monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
+
+  l = g_list_copy (monitor->volumes);
+  g_list_foreach (l, (GFunc)g_object_ref, NULL);
+
+  return l;
+}
+
+static GList *
+get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+  GUnixVolumeMonitor *monitor;
+  GList *l;
+  
+  monitor = G_UNIX_VOLUME_MONITOR (volume_monitor);
+
+  l = g_list_copy (monitor->drives);
+  g_list_foreach (l, (GFunc)g_object_ref, NULL);
+
+  return l;
+}
+
+static GVolume *
+get_volume_for_mountpoint (const char *mountpoint)
+{
+  GUnixMount *mount;
+  GUnixVolume *volume;
+
+  mount = g_get_unix_mount_at (mountpoint, NULL);
+  
+  /* TODO: Set drive? */
+  volume = g_unix_volume_new (mount, NULL);
+
+  return G_VOLUME (volume);
+}
+
+static void
+g_unix_volume_monitor_class_init (GUnixVolumeMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  GVolumeMonitorClass *monitor_class = G_VOLUME_MONITOR_CLASS (klass);
+  GNativeVolumeMonitorClass *native_class = G_NATIVE_VOLUME_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_unix_volume_monitor_finalize;
+
+  monitor_class->get_mounted_volumes = get_mounted_volumes;
+  monitor_class->get_connected_drives = get_connected_drives;
+
+  native_class->priority = 0;
+  native_class->get_volume_for_mountpoint = get_volume_for_mountpoint;
+}
+
+static void
+mountpoints_changed (GUnixMountMonitor *mount_monitor,
+                    gpointer user_data)
+{
+  GUnixVolumeMonitor *unix_monitor = user_data;
+
+  /* Update both to make sure drives are created before volumes */
+  update_drives (unix_monitor);
+  update_volumes (unix_monitor);
+}
+
+static void
+mounts_changed (GUnixMountMonitor *mount_monitor,
+               gpointer user_data)
+{
+  GUnixVolumeMonitor *unix_monitor = user_data;
+
+  /* Update both to make sure drives are created before volumes */
+  update_drives (unix_monitor);
+  update_volumes (unix_monitor);
+}
+
+static void
+g_unix_volume_monitor_init (GUnixVolumeMonitor *unix_monitor)
+{
+
+  unix_monitor->mount_monitor = g_unix_mount_monitor_new ();
+
+  g_signal_connect (unix_monitor->mount_monitor,
+                   "mounts_changed", G_CALLBACK (mounts_changed),
+                   unix_monitor);
+  
+  g_signal_connect (unix_monitor->mount_monitor,
+                   "mountpoints_changed", G_CALLBACK (mountpoints_changed),
+                   unix_monitor);
+                   
+  update_drives (unix_monitor);
+  update_volumes (unix_monitor);
+
+}
+
+/**
+ * g_unix_volume_monitor_new:
+ * 
+ * Returns:  a new #GVolumeMonitor.
+ **/
+GVolumeMonitor *
+g_unix_volume_monitor_new (void)
+{
+  GUnixVolumeMonitor *monitor;
+
+  monitor = g_object_new (G_TYPE_UNIX_VOLUME_MONITOR, NULL);
+  
+  return G_VOLUME_MONITOR (monitor);
+}
+
+static void
+diff_sorted_lists (GList *list1, GList *list2, GCompareFunc compare,
+                  GList **added, GList **removed)
+{
+  int order;
+  
+  *added = *removed = NULL;
+  
+  while (list1 != NULL &&
+        list2 != NULL)
+    {
+      order = (*compare) (list1->data, list2->data);
+      if (order < 0)
+       {
+         *removed = g_list_prepend (*removed, list1->data);
+         list1 = list1->next;
+       }
+      else if (order > 0)
+       {
+         *added = g_list_prepend (*added, list2->data);
+         list2 = list2->next;
+       }
+      else
+       { /* same item */
+         list1 = list1->next;
+         list2 = list2->next;
+       }
+    }
+
+  while (list1 != NULL)
+    {
+      *removed = g_list_prepend (*removed, list1->data);
+      list1 = list1->next;
+    }
+  while (list2 != NULL)
+    {
+      *added = g_list_prepend (*added, list2->data);
+      list2 = list2->next;
+    }
+}
+
+/**
+ * g_unix_volume_lookup_drive_for_mountpoint: 
+ * @monitor:
+ * @mountpoint:
+ * 
+ * Returns:  #GUnixDrive for the given @mountpoint.
+ **/
+GUnixDrive *
+g_unix_volume_monitor_lookup_drive_for_mountpoint (GUnixVolumeMonitor *monitor,
+                                                  const char *mountpoint)
+{
+  GList *l;
+
+  for (l = monitor->drives; l != NULL; l = l->next)
+    {
+      GUnixDrive *drive = l->data;
+
+      if (g_unix_drive_has_mountpoint (drive, mountpoint))
+       return drive;
+    }
+  
+  return NULL;
+}
+
+static GUnixVolume *
+find_volume_by_mountpoint (GUnixVolumeMonitor *monitor,
+                          const char *mountpoint)
+{
+  GList *l;
+
+  for (l = monitor->volumes; l != NULL; l = l->next)
+    {
+      GUnixVolume *volume = l->data;
+
+      if (g_unix_volume_has_mountpoint (volume, mountpoint))
+       return volume;
+    }
+  
+  return NULL;
+}
+
+static void
+update_drives (GUnixVolumeMonitor *monitor)
+{
+  GList *new_mountpoints;
+  GList *removed, *added;
+  GList *l;
+  GUnixDrive *drive;
+  
+  new_mountpoints = g_get_unix_mount_points (NULL);
+  
+  new_mountpoints = g_list_sort (new_mountpoints, (GCompareFunc) g_unix_mount_point_compare);
+  
+  diff_sorted_lists (monitor->last_mountpoints,
+                    new_mountpoints, (GCompareFunc) g_unix_mount_point_compare,
+                    &added, &removed);
+  
+  for (l = removed; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mountpoint = l->data;
+      
+      drive = g_unix_volume_monitor_lookup_drive_for_mountpoint (monitor,
+                                                                g_unix_mount_point_get_mount_path (mountpoint));
+      if (drive)
+       {
+         g_unix_drive_disconnected (drive);
+         monitor->drives = g_list_remove (monitor->drives, drive);
+         g_signal_emit_by_name (monitor, "drive_disconnected", drive);
+         g_object_unref (drive);
+       }
+    }
+  
+  for (l = added; l != NULL; l = l->next)
+    {
+      GUnixMountPoint *mountpoint = l->data;
+      
+      drive = g_unix_drive_new (G_VOLUME_MONITOR (monitor), mountpoint);
+      if (drive)
+       {
+         monitor->drives = g_list_prepend (monitor->drives, drive);
+         g_signal_emit_by_name (monitor, "drive_connected", drive);
+       }
+    }
+  
+  g_list_free (added);
+  g_list_free (removed);
+  g_list_foreach (monitor->last_mountpoints,
+                 (GFunc)g_unix_mount_point_free, NULL);
+  g_list_free (monitor->last_mountpoints);
+  monitor->last_mountpoints = new_mountpoints;
+}
+
+static void
+update_volumes (GUnixVolumeMonitor *monitor)
+{
+  GList *new_mounts;
+  GList *removed, *added;
+  GList *l;
+  GUnixVolume *volume;
+  GUnixDrive *drive;
+  const char *mount_path;
+  
+  new_mounts = g_get_unix_mounts (NULL);
+  
+  new_mounts = g_list_sort (new_mounts, (GCompareFunc) g_unix_mount_compare);
+  
+  diff_sorted_lists (monitor->last_mounts,
+                    new_mounts, (GCompareFunc) g_unix_mount_compare,
+                    &added, &removed);
+  
+  for (l = removed; l != NULL; l = l->next)
+    {
+      GUnixMount *mount = l->data;
+      
+      volume = find_volume_by_mountpoint (monitor, g_unix_mount_get_mount_path (mount));
+      if (volume)
+       {
+         g_unix_volume_unmounted (volume);
+         monitor->volumes = g_list_remove (monitor->volumes, volume);
+         g_signal_emit_by_name (monitor, "volume_unmounted", volume);
+         g_object_unref (volume);
+       }
+    }
+  
+  for (l = added; l != NULL; l = l->next)
+    {
+      GUnixMount *mount = l->data;
+
+      mount_path = g_unix_mount_get_mount_path (mount);
+      
+      drive = g_unix_volume_monitor_lookup_drive_for_mountpoint (monitor,
+                                                                mount_path);
+      volume = g_unix_volume_new (mount, drive);
+      if (volume)
+       {
+         monitor->volumes = g_list_prepend (monitor->volumes, volume);
+         g_signal_emit_by_name (monitor, "volume_mounted", volume);
+       }
+    }
+  
+  g_list_free (added);
+  g_list_free (removed);
+  g_list_foreach (monitor->last_mounts,
+                 (GFunc)g_unix_mount_free, NULL);
+  g_list_free (monitor->last_mounts);
+  monitor->last_mounts = new_mounts;
+}
diff --git a/gio/gunixvolumemonitor.h b/gio/gunixvolumemonitor.h
new file mode 100644 (file)
index 0000000..feb9409
--- /dev/null
@@ -0,0 +1,57 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_UNIX_VOLUME_MONITOR_H__
+#define __G_UNIX_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gnativevolumemonitor.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_UNIX_VOLUME_MONITOR        (g_unix_volume_monitor_get_type ())
+#define G_UNIX_VOLUME_MONITOR(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_UNIX_VOLUME_MONITOR, GUnixVolumeMonitor))
+#define G_UNIX_VOLUME_MONITOR_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_UNIX_VOLUME_MONITOR, GUnixVolumeMonitorClass))
+#define G_IS_UNIX_VOLUME_MONITOR(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_UNIX_VOLUME_MONITOR))
+#define G_IS_UNIX_VOLUME_MONITOR_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_UNIX_VOLUME_MONITOR))
+
+typedef struct _GUnixVolumeMonitor GUnixVolumeMonitor;
+typedef struct _GUnixVolumeMonitorClass GUnixVolumeMonitorClass;
+
+/* Forward definitions */
+typedef struct _GUnixVolume GUnixVolume;
+typedef struct _GUnixDrive GUnixDrive;
+
+struct _GUnixVolumeMonitorClass {
+  GNativeVolumeMonitorClass parent_class;
+
+};
+
+GType g_unix_volume_monitor_get_type (void) G_GNUC_CONST;
+
+GVolumeMonitor *g_unix_volume_monitor_new                         (void);
+GUnixDrive *    g_unix_volume_monitor_lookup_drive_for_mountpoint (GUnixVolumeMonitor *monitor,
+                                                                  const char         *mountpoint);
+
+G_END_DECLS
+
+#endif /* __G_UNIX_VOLUME_MONITOR_H__ */
diff --git a/gio/gurifuncs.c b/gio/gurifuncs.c
new file mode 100644 (file)
index 0000000..2e10723
--- /dev/null
@@ -0,0 +1,276 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gurifuncs.h"
+#include "string.h"
+
+static int
+unescape_character (const char *scanner)
+{
+  int first_digit;
+  int second_digit;
+  
+  first_digit = g_ascii_xdigit_value (*scanner++);
+  if (first_digit < 0)
+    return -1;
+
+  second_digit = g_ascii_xdigit_value (*scanner++);
+  if (second_digit < 0)
+    return -1;
+
+  return (first_digit << 4) | second_digit;
+}
+
+/**
+ * g_uri_unescape_segment:
+ * @escaped_string: a string.
+ * @escaped_string_end: a string.
+ * @illegal_characters: a string of illegal characters not to be allowed.
+ * 
+ * Returns: an unescaped version of @escaped_string or %NULL on error.
+ * The returned string should be freed when no longer needed.
+ **/
+char *
+g_uri_unescape_segment (const char *escaped_string,
+                       const char *escaped_string_end,
+                       const char *illegal_characters)
+{
+  const char *in;
+  char *out, *result;
+  gint character;
+  
+  if (escaped_string == NULL)
+    return NULL;
+  
+  if (escaped_string_end == NULL)
+    escaped_string_end = escaped_string + strlen (escaped_string);
+  
+  result = g_malloc (escaped_string_end - escaped_string + 1);
+  
+  out = result;
+  for (in = escaped_string; in < escaped_string_end; in++)
+    {
+      character = *in;
+      
+      if (*in == '%')
+       {
+         in++;
+         
+         if (escaped_string_end - in < 2)
+           {
+             /* Invalid escaped char (to short) */
+             g_free (result);
+             return NULL;
+           }
+         
+         character = unescape_character (in);
+         
+         /* Check for an illegal character. We consider '\0' illegal here. */
+         if (character <= 0 ||
+             (illegal_characters != NULL &&
+              strchr (illegal_characters, (char)character) != NULL))
+           {
+             g_free (result);
+             return NULL;
+           }
+         
+         in++; /* The other char will be eaten in the loop header */
+       }
+      *out++ = (char)character;
+    }
+  
+  *out = '\0';
+  
+  return result;
+}
+
+/**
+ * g_uri_unescape_string:
+ * @escaped_string: an escaped string to be unescaped.
+ * @illegal_characters: a string of illegal characters not to be allowed.
+ * 
+ * Returns: an unescaped version of @escaped_string. 
+ *
+ * The returned string should be freed when no longer needed
+ *
+ **/
+char *
+g_uri_unescape_string (const char *escaped_string,
+                      const char *illegal_characters)
+{
+  return g_uri_unescape_segment (escaped_string, NULL, illegal_characters);
+}
+
+/**
+ * g_uri_get_scheme:
+ * @uri: a valid URI.
+ * 
+ * Returns: The "Scheme" component of the URI, or %NULL on error. 
+ * RFC 3986 decodes the scheme as:
+ * URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] 
+ * Popular schemes include "file", "http", "svn", etc.
+ *
+ * The returned string should be freed when no longer needed.
+ *
+ **/
+char *
+g_uri_get_scheme (const char  *uri)
+{
+  const char *p;
+  char c;
+
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  /* From RFC 3986 Decodes:
+   * URI         = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
+   */ 
+
+  p = uri;
+  
+  /* Decode scheme:
+     scheme      = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
+  */
+
+  if (!g_ascii_isalpha (*p))
+    return NULL;
+  
+  while (1)
+    {
+      c = *p++;
+      
+      if (c == ':')
+       break;
+      
+      if (!(g_ascii_isalnum(c) ||
+           c == '+' ||
+           c == '-' ||
+           c == '.'))
+       return NULL;
+    }
+  
+  return g_strndup (uri, p - uri - 1);
+}
+
+#define SUB_DELIM_CHARS  "!$&'()*+,;="
+
+static gboolean
+is_valid (char c, const char *reserved_chars_allowed)
+{
+  if (g_ascii_isalnum (c) ||
+      c == '-' ||
+      c == '.' ||
+      c == '_' ||
+      c == '~')
+    return TRUE;
+
+  if (reserved_chars_allowed &&
+      strchr (reserved_chars_allowed, c) != NULL)
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean 
+gunichar_ok (gunichar c)
+{
+  return
+    (c != (gunichar) -2) &&
+    (c != (gunichar) -1);
+}
+
+/**
+ * g_string_append_uri_escaped:
+ * @string: a #GString to append to.
+ * @unescaped: the input C string of unescaped URI data.
+ * @reserved_chars_allowed: a string of reserve characters allowed to be used.
+ * @allow_utf8: set %TRUE if the return value may include UTF8 characters.
+ * 
+ * Returns a #GString with the escaped URI appended.
+ *
+ **/
+GString *
+g_string_append_uri_escaped (GString *string,
+                            const char *unescaped,
+                            const char *reserved_chars_allowed,
+                            gboolean allow_utf8)
+{
+  unsigned char c;
+  const char *end;
+  static const gchar hex[16] = "0123456789ABCDEF";
+
+  g_return_val_if_fail (string != NULL, NULL);
+  g_return_val_if_fail (unescaped != NULL, NULL);
+
+  end = unescaped + strlen (unescaped);
+  
+  while ((c = *unescaped) != 0)
+    {
+      if (c >= 0x80 && allow_utf8 &&
+         gunichar_ok (g_utf8_get_char_validated (unescaped, end - unescaped)))
+       {
+         int len = g_utf8_skip [c];
+         g_string_append_len (string, unescaped, len);
+         unescaped += len;
+       }
+      else if (is_valid (c, reserved_chars_allowed))
+       {
+         g_string_append_c (string, c);
+         unescaped++;
+       }
+      else
+       {
+         g_string_append_c (string, '%');
+         g_string_append_c (string, hex[((guchar)c) >> 4]);
+         g_string_append_c (string, hex[((guchar)c) & 0xf]);
+         unescaped++;
+       }
+    }
+
+  return string;
+}
+
+/**
+ * g_uri_escape_string:
+ * @unescaped: the unescaped input string.
+ * @reserved_chars_allowed: a string of reserve characters allowed to be used.
+ * @allow_utf8: set to %TRUE if string can include UTF8 characters.
+ * 
+ * Returns an escaped version of @unescaped. 
+ * 
+ * The returned string should be freed when no longer needed.
+ **/
+char *
+g_uri_escape_string (const char *unescaped,
+                    const char  *reserved_chars_allowed,
+                    gboolean     allow_utf8)
+{
+  GString *s;
+
+  g_return_val_if_fail (unescaped != NULL, NULL);
+
+  s = g_string_sized_new (strlen (unescaped) + 10);
+  
+  g_string_append_uri_escaped (s, unescaped, reserved_chars_allowed, allow_utf8);
+  
+  return g_string_free (s, FALSE);
+}
diff --git a/gio/gurifuncs.h b/gio/gurifuncs.h
new file mode 100644 (file)
index 0000000..cace2d9
--- /dev/null
@@ -0,0 +1,55 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_URI_FUNCS_H__
+#define __G_URI_FUNCS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#define G_URI_RESERVED_CHARS_GENERIC_DELIMITERS ":/?#[]@"
+#define G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS "!$&'()*+,;="
+#define G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS ":@"
+#define G_URI_RESERVED_CHARS_ALLOWED_IN_PATH G_URI_RESERVED_CHARS_ALLOWED_IN_PATH_ELEMENT "/"
+#define G_URI_RESERVED_CHARS_ALLOWED_IN_USERINFO G_URI_RESERVED_CHARS_SUBCOMPONENT_DELIMITERS ":"
+
+char *   g_uri_unescape_string       (const char *escaped_string,
+                                     const char *illegal_characters);
+char *   g_uri_unescape_segment      (const char *escaped_string,
+                                     const char *escaped_string_end,
+                                     const char *illegal_characters);
+char *   g_uri_get_scheme            (const char *uri);
+char *   g_uri_escape_string         (const char *unescaped,
+                                     const char *reserved_chars_allowed,
+                                     gboolean    allow_utf8);
+GString *g_string_append_uri_escaped (GString    *string,
+                                     const char *unescaped,
+                                     const char *reserved_chars_allowed,
+                                     gboolean    allow_utf8);
+
+
+
+
+G_END_DECLS
+
+#endif /* __G_URI_FUNCS_H__ */
diff --git a/gio/gvfs.c b/gio/gvfs.c
new file mode 100644 (file)
index 0000000..4dc1f25
--- /dev/null
@@ -0,0 +1,258 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include <string.h>
+#include "gvfs.h"
+#include "glocalvfs.h"
+#include "giomodule.h"
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GVfs, g_vfs, G_TYPE_OBJECT);
+
+static void
+g_vfs_class_init (GVfsClass *klass)
+{
+}
+
+static void
+g_vfs_init (GVfs *vfs)
+{
+}
+
+/**
+ * g_vfs_is_active:
+ * @vfs: an  #GVfs.
+ * 
+ * Returns TRUE if construction of the @vfs was successful and its now active.
+ **/
+gboolean
+g_vfs_is_active (GVfs *vfs)
+{
+  GVfsClass *class;
+
+  g_return_val_if_fail (G_IS_VFS (vfs), FALSE);
+
+  class = G_VFS_GET_CLASS (vfs);
+
+  return (* class->is_active) (vfs);
+}
+
+
+/**
+ * g_vfs_get_file_for_path:
+ * @vfs: an input #GVfs.
+ * @path: a string containing a VFS path.
+ * 
+ * Returns a #GFile for the given @path.
+ * 
+ **/
+GFile *
+g_vfs_get_file_for_path (GVfs *vfs,
+                        const char *path)
+{
+  GVfsClass *class;
+  
+  g_return_val_if_fail (G_IS_VFS (vfs), NULL);
+  g_return_val_if_fail (path != NULL, NULL);
+
+  class = G_VFS_GET_CLASS (vfs);
+
+  return (* class->get_file_for_path) (vfs, path);
+}
+
+/**
+ * g_vfs_get_file_for_uri:
+ * @vfs: an input #GVfs.
+ * @uri: an input string containing a URI path.
+ *
+ * This operation never fails, but the returned object
+ * might not support any I/O operation if the uri
+ * is malformed or if the uri type is not supported.
+ * 
+ * Returns a #GFile for the given @uri. 
+ * 
+ **/
+GFile *
+g_vfs_get_file_for_uri (GVfs *vfs,
+                       const char *uri)
+{
+  GVfsClass *class;
+  
+  g_return_val_if_fail (G_IS_VFS (vfs), NULL);
+  g_return_val_if_fail (uri != NULL, NULL);
+
+  class = G_VFS_GET_CLASS (vfs);
+
+  return (* class->get_file_for_uri) (vfs, uri);
+}
+
+/**
+ * g_vfs_get_supported_uri_schemes:
+ * @vfs: an input #GVfs.
+ * 
+ * Returns: 
+ * 
+ **/
+const gchar * const *
+g_vfs_get_supported_uri_schemes (GVfs *vfs)
+{
+  GVfsClass *class;
+  
+  g_return_val_if_fail (G_IS_VFS (vfs), NULL);
+
+  class = G_VFS_GET_CLASS (vfs);
+
+  return (* class->get_supported_uri_schemes) (vfs);
+}
+
+/**
+ * g_vfs_parse_name:
+ * @vfs: an input #GVfs.
+ * @parse_name: a string to be parsed by the VFS module.
+ * 
+ * This operation never fails, but the returned object might 
+ * not support any I/O operations if the @parse_name cannot 
+ * be parsed by the #GVfs module.
+ * 
+ * Returns a #GFile for the given @parse_name.
+ * 
+ **/
+GFile *
+g_vfs_parse_name (GVfs *vfs,
+                 const char *parse_name)
+{
+  GVfsClass *class;
+  
+  g_return_val_if_fail (G_IS_VFS (vfs), NULL);
+  g_return_val_if_fail (parse_name != NULL, NULL);
+
+  class = G_VFS_GET_CLASS (vfs);
+
+  return (* class->parse_name) (vfs, parse_name);
+}
+
+/* Note: This compares in reverse order.
+   Higher prio -> sort first
+ */
+static gint
+compare_vfs_type (gconstpointer  a,
+                 gconstpointer  b,
+                 gpointer       user_data)
+{
+  GVfsClass *class_a, *class_b;
+  gint res;
+  const char *use_this_vfs;
+  
+  class_a = g_type_class_ref (*(GType *)a);
+  class_b = g_type_class_ref (*(GType *)b);
+  use_this_vfs = user_data;
+
+  if (class_a == class_b)
+    res = 0;
+  else if (use_this_vfs != NULL &&
+          strcmp (class_a->name, use_this_vfs) == 0)
+    res = -1;
+  else if (use_this_vfs != NULL &&
+          strcmp (class_b->name, use_this_vfs) == 0)
+    res = 1;
+  else 
+    res = class_b->priority - class_a->priority;
+  
+  g_type_class_unref (class_a);
+  g_type_class_unref (class_b);
+  
+  return res;
+}
+
+
+static gpointer
+get_default_vfs (gpointer arg)
+{
+  volatile GType local_type;
+  GType *vfs_impls;
+  int i;
+  guint n_vfs_impls;
+  const char *use_this;
+  GVfs *vfs;
+  GType (*casted_get_type)(void);
+
+  use_this = g_getenv ("GIO_USE_VFS");
+  
+  /* Ensure GLocalVfs type is available
+     the cast is required to avoid any G_GNUC_CONST optimizations */
+  casted_get_type = g_local_vfs_get_type;
+  local_type = casted_get_type ();
+  
+  /* Ensure vfs in modules loaded */
+  g_io_modules_ensure_loaded (GIO_MODULE_DIR);
+
+  vfs_impls = g_type_children (G_TYPE_VFS, &n_vfs_impls);
+
+  g_qsort_with_data (vfs_impls, n_vfs_impls, sizeof (GType),
+                    compare_vfs_type, (gpointer)use_this);
+  
+  for (i = 0; i < n_vfs_impls; i++)
+    {
+      vfs = g_object_new (vfs_impls[i], NULL);
+
+      if (g_vfs_is_active (vfs))
+       break;
+
+      g_object_unref (vfs);
+      vfs = NULL;
+    }
+  
+  g_free (vfs_impls);
+
+  return vfs;
+}
+
+/**
+ * g_vfs_get_default:
+ * 
+ * Returns the default #GVfs for the system.
+ **/
+GVfs *
+g_vfs_get_default (void)
+{
+  static GOnce once_init = G_ONCE_INIT;
+  
+  return g_once (&once_init, get_default_vfs, NULL);
+}
+
+/**
+ * g_vfs_get_local:
+ * 
+ * Returns the local #GVfs for the system.
+ **/
+GVfs *
+g_vfs_get_local (void)
+{
+  static gsize vfs = 0;
+
+  if (g_once_init_enter (&vfs))
+    g_once_init_leave (&vfs, (gsize)g_local_vfs_new ());
+
+  return G_VFS (vfs);
+}
+
diff --git a/gio/gvfs.h b/gio/gvfs.h
new file mode 100644 (file)
index 0000000..ebc82bd
--- /dev/null
@@ -0,0 +1,98 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VFS_IMPLEMENTATION_H__
+#define __G_VFS_IMPLEMENTATION_H__
+
+#include <glib-object.h>
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_VFS         (g_vfs_get_type ())
+#define G_VFS(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_VFS, GVfs))
+#define G_VFS_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_VFS, GVfsClass))
+#define G_VFS_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_VFS, GVfsClass))
+#define G_IS_VFS(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_VFS))
+#define G_IS_VFS_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_VFS))
+
+typedef struct _GVfs         GVfs; /* Dummy typedef */
+typedef struct _GVfsClass    GVfsClass;
+
+struct _GVfs {
+  GObject parent;
+};
+
+struct _GVfsClass
+{
+  GObjectClass parent_class;
+
+  const char *name;
+  int priority;
+  
+  /* Virtual Table */
+
+  gboolean             (*is_active)                 (GVfs *vfs);
+  GFile               *(*get_file_for_path)         (GVfs *vfs,
+                                                    const char *path);
+  GFile               *(*get_file_for_uri)          (GVfs *vfs,
+                                                    const char *uri);
+  const gchar * const *(*get_supported_uri_schemes) (GVfs *vfs);
+  GFile               *(*parse_name)                (GVfs *vfs,
+                                                    const char *parse_name);
+
+
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+  void (*_g_reserved9) (void);
+  void (*_g_reserved10) (void);
+  void (*_g_reserved11) (void);
+  void (*_g_reserved12) (void);
+  
+};
+
+GType g_vfs_get_type (void) G_GNUC_CONST;
+
+gboolean              g_vfs_is_active                 (GVfs       *vfs);
+GFile *               g_vfs_get_file_for_path         (GVfs       *vfs,
+                                                      const char *path);
+GFile *               g_vfs_get_file_for_uri          (GVfs       *vfs,
+                                                      const char *uri);
+const gchar * const * g_vfs_get_supported_uri_schemes (GVfs *vfs);
+
+GFile *               g_vfs_parse_name                (GVfs       *vfs,
+                                                      const char *parse_name);
+
+GVfs *                g_vfs_get_default               (void);
+GVfs *                g_vfs_get_local                 (void);
+
+G_END_DECLS
+
+#endif /* __G_VFS_H__ */
diff --git a/gio/gvolume.c b/gio/gvolume.c
new file mode 100644 (file)
index 0000000..280f46c
--- /dev/null
@@ -0,0 +1,326 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gvolume.h"
+#include "gvolumeprivate.h"
+#include "gsimpleasyncresult.h"
+#include "glibintl.h"
+
+static void g_volume_base_init (gpointer g_class);
+static void g_volume_class_init (gpointer g_class,
+                                gpointer class_data);
+
+GType
+g_volume_get_type (void)
+{
+  static GType volume_type = 0;
+
+  if (! volume_type)
+    {
+      static const GTypeInfo volume_info =
+      {
+        sizeof (GVolumeIface), /* class_size */
+       g_volume_base_init,   /* base_init */
+       NULL,           /* base_finalize */
+       g_volume_class_init,
+       NULL,           /* class_finalize */
+       NULL,           /* class_data */
+       0,
+       0,              /* n_preallocs */
+       NULL
+      };
+
+      volume_type =
+       g_type_register_static (G_TYPE_INTERFACE, I_("GVolume"),
+                               &volume_info, 0);
+
+      g_type_interface_add_prerequisite (volume_type, G_TYPE_OBJECT);
+    }
+
+  return volume_type;
+}
+
+static void
+g_volume_class_init (gpointer g_class,
+                  gpointer class_data)
+{
+}
+
+static void
+g_volume_base_init (gpointer g_class)
+{
+  static gboolean initialized = FALSE;
+
+  if (! initialized)
+    {
+      g_signal_new (I_("changed"),
+                    G_TYPE_VOLUME,
+                    G_SIGNAL_RUN_LAST,
+                    G_STRUCT_OFFSET (GVolumeIface, changed),
+                    NULL, NULL,
+                    g_cclosure_marshal_VOID__VOID,
+                    G_TYPE_NONE, 0);
+
+      initialized = TRUE;
+    }
+}
+
+/**
+ * g_volume_get_root:
+ * @volume: a #GVolume.
+ * 
+ * Returns a #GFile.
+ * 
+ **/
+GFile *
+g_volume_get_root (GVolume *volume)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  return (* iface->get_root) (volume);
+}
+
+/**
+ * g_volume_get_name:
+ * @volume: a #GVolume.
+ * 
+ * Returns the name for the given @volume. 
+ * 
+ * The returned string should be freed when no longer needed.
+ * 
+ **/
+char *
+g_volume_get_name (GVolume *volume)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  return (* iface->get_name) (volume);
+}
+
+/**
+ * g_volume_get_icon:
+ * @volume:
+ * 
+ * Returns the #GIcon for the given @volume.
+ * 
+ **/
+GIcon *
+g_volume_get_icon (GVolume *volume)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  return (* iface->get_icon) (volume);
+}
+  
+/**
+ * g_volume_get_drive:
+ * @volume:
+ * 
+ * Returns the #GDrive for the given @volume.
+ * 
+ **/
+GDrive *
+g_volume_get_drive (GVolume *volume)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), NULL);
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  return (* iface->get_drive) (volume);
+}
+
+/**
+ * g_volume_can_unmount: 
+ * @volume:
+ * 
+ * Returns %TRUE if the @volume can be unmounted.
+ **/
+gboolean
+g_volume_can_unmount (GVolume *volume)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  return (* iface->can_unmount) (volume);
+}
+
+/**
+ * g_volume_can_eject:
+ * @volume:
+ * 
+ * Returns %TRUE if the @volume can be ejected.
+ * 
+ **/
+gboolean
+g_volume_can_eject (GVolume *volume)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  return (* iface->can_eject) (volume);
+}
+
+/**
+ * g_volume_unmount:
+ * @volume:
+ * @callback:
+ * @user_data:
+ * 
+ * 
+ **/
+void
+g_volume_unmount (GVolume *volume,
+                 GCancellable *cancellable,
+                 GAsyncReadyCallback callback,
+                 gpointer user_data)
+{
+  GVolumeIface *iface;
+
+  g_return_if_fail (G_IS_VOLUME (volume));
+  
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  if (iface->unmount == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (volume),
+                                          callback, user_data,
+                                          G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                          _("volume doesn't implement unmount"));
+      
+      return;
+    }
+  
+  (* iface->unmount) (volume, cancellable, callback, user_data);
+}
+
+/**
+ * g_volume_unmount_finish:
+ * @volume:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Return:
+ * 
+ **/
+gboolean
+g_volume_unmount_finish (GVolume              *volume,
+                        GAsyncResult         *result,
+                        GError              **error)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_VOLUME_GET_IFACE (volume);
+  return (* iface->unmount_finish) (volume, result, error);
+}
+
+/**
+ * g_volume_eject:
+ * @volume:
+ * @callback:
+ * @user_data:
+ * 
+ **/
+void
+g_volume_eject (GVolume         *volume,
+               GCancellable *cancellable,
+               GAsyncReadyCallback  callback,
+               gpointer         user_data)
+{
+  GVolumeIface *iface;
+
+  g_return_if_fail (G_IS_VOLUME (volume));
+
+  iface = G_VOLUME_GET_IFACE (volume);
+
+  if (iface->eject == NULL)
+    {
+      g_simple_async_report_error_in_idle (G_OBJECT (volume),
+                                          callback, user_data,
+                                          G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+                                          _("volume doesn't implement eject"));
+      
+      return;
+    }
+  
+  (* iface->eject) (volume, cancellable, callback, user_data);
+}
+
+/**
+ * g_volume_eject_finish:
+ * @volume:
+ * @result:
+ * @error: a #GError location to store the error occuring, or %NULL to 
+ * ignore.
+ * Returns: 
+ * 
+ **/
+gboolean
+g_volume_eject_finish (GVolume              *volume,
+                      GAsyncResult         *result,
+                      GError              **error)
+{
+  GVolumeIface *iface;
+
+  g_return_val_if_fail (G_IS_VOLUME (volume), FALSE);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+
+  if (G_IS_SIMPLE_ASYNC_RESULT (result))
+    {
+      GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
+      if (g_simple_async_result_propagate_error (simple, error))
+       return FALSE;
+    }
+  
+  iface = G_VOLUME_GET_IFACE (volume);
+  return (* iface->eject_finish) (volume, result, error);
+}
diff --git a/gio/gvolume.h b/gio/gvolume.h
new file mode 100644 (file)
index 0000000..38addbe
--- /dev/null
@@ -0,0 +1,97 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VOLUME_H__
+#define __G_VOLUME_H__
+
+#include <glib-object.h>
+#include <gio/gfile.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_VOLUME            (g_volume_get_type ())
+#define G_VOLUME(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), G_TYPE_VOLUME, GVolume))
+#define G_IS_VOLUME(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), G_TYPE_VOLUME))
+#define G_VOLUME_GET_IFACE(obj)  (G_TYPE_INSTANCE_GET_INTERFACE ((obj), G_TYPE_VOLUME, GVolumeIface))
+
+/* GVolume typedef is in gfile.h due to include order issues */
+typedef struct _GDrive          GDrive; /* Dummy typedef */
+typedef struct _GVolumeIface    GVolumeIface;
+
+struct _GVolumeIface
+{
+  GTypeInterface g_iface;
+
+  /* signals */
+
+  void (*changed) (GVolume *volume);
+  
+  /* Virtual Table */
+
+  GFile *  (*get_root)       (GVolume         *volume);
+  char *   (*get_name)       (GVolume         *volume);
+  GIcon *  (*get_icon)       (GVolume         *volume);
+  GDrive * (*get_drive)      (GVolume         *volume);
+  gboolean (*can_unmount)    (GVolume         *volume);
+  gboolean (*can_eject)      (GVolume         *volume);
+  void     (*unmount)        (GVolume         *volume,
+                             GCancellable    *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer         user_data);
+  gboolean (*unmount_finish) (GVolume         *volume,
+                             GAsyncResult    *result,
+                             GError         **error);
+  void     (*eject)          (GVolume         *volume,
+                             GCancellable    *cancellable,
+                             GAsyncReadyCallback callback,
+                             gpointer         user_data);
+  gboolean (*eject_finish)   (GVolume         *volume,
+                             GAsyncResult    *result,
+                             GError         **error);
+};
+
+GType g_volume_get_type (void) G_GNUC_CONST;
+
+GFile   *g_volume_get_root       (GVolume              *volume);
+char *   g_volume_get_name       (GVolume              *volume);
+GIcon *  g_volume_get_icon       (GVolume              *volume);
+GDrive * g_volume_get_drive      (GVolume              *volume);
+gboolean g_volume_can_unmount    (GVolume              *volume);
+gboolean g_volume_can_eject      (GVolume              *volume);
+void     g_volume_unmount        (GVolume              *volume,
+                                 GCancellable         *cancellable,
+                                 GAsyncReadyCallback   callback,
+                                 gpointer              user_data);
+gboolean g_volume_unmount_finish (GVolume              *volume,
+                                 GAsyncResult         *result,
+                                 GError              **error);
+void     g_volume_eject          (GVolume              *volume,
+                                 GCancellable         *cancellable,
+                                 GAsyncReadyCallback   callback,
+                                 gpointer              user_data);
+gboolean g_volume_eject_finish   (GVolume              *volume,
+                                 GAsyncResult         *result,
+                                 GError              **error);
+
+G_END_DECLS
+
+#endif /* __G_VOLUME_H__ */
diff --git a/gio/gvolumemonitor.c b/gio/gvolumemonitor.c
new file mode 100644 (file)
index 0000000..a1b7c9b
--- /dev/null
@@ -0,0 +1,143 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+#include "gvolumemonitor.h"
+#include "glibintl.h"
+
+G_DEFINE_TYPE (GVolumeMonitor, g_volume_monitor, G_TYPE_OBJECT);
+
+enum {
+  VOLUME_MOUNTED,
+  VOLUME_PRE_UNMOUNT,
+  VOLUME_UNMOUNTED,
+  DRIVE_CONNECTED,
+  DRIVE_DISCONNECTED,
+  LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+static void
+g_volume_monitor_finalize (GObject *object)
+{
+  GVolumeMonitor *monitor;
+
+  monitor = G_VOLUME_MONITOR (object);
+
+  if (G_OBJECT_CLASS (g_volume_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_volume_monitor_parent_class)->finalize) (object);
+}
+
+static void
+g_volume_monitor_class_init (GVolumeMonitorClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_volume_monitor_finalize;
+
+  signals[VOLUME_MOUNTED] = g_signal_new (I_("volume_mounted"),
+                                         G_TYPE_VOLUME_MONITOR,
+                                         G_SIGNAL_RUN_LAST,
+                                         G_STRUCT_OFFSET (GVolumeMonitorClass, volume_mounted),
+                                         NULL, NULL,
+                                         g_cclosure_marshal_VOID__OBJECT,
+                                         G_TYPE_NONE, 1, G_TYPE_VOLUME);
+  
+  signals[VOLUME_PRE_UNMOUNT] = g_signal_new (I_("volume_pre_unmount"),
+                                             G_TYPE_VOLUME_MONITOR,
+                                             G_SIGNAL_RUN_LAST,
+                                             G_STRUCT_OFFSET (GVolumeMonitorClass, volume_pre_unmount),
+                                             NULL, NULL,
+                                             g_cclosure_marshal_VOID__OBJECT,
+                                             G_TYPE_NONE, 1, G_TYPE_VOLUME);
+  
+  signals[VOLUME_UNMOUNTED] = g_signal_new (I_("volume_unmounted"),
+                                           G_TYPE_VOLUME_MONITOR,
+                                           G_SIGNAL_RUN_LAST,
+                                           G_STRUCT_OFFSET (GVolumeMonitorClass, volume_unmounted),
+                                           NULL, NULL,
+                                           g_cclosure_marshal_VOID__OBJECT,
+                                           G_TYPE_NONE, 1, G_TYPE_VOLUME);
+
+  signals[DRIVE_CONNECTED] = g_signal_new (I_("drive_connected"),
+                                          G_TYPE_VOLUME_MONITOR,
+                                          G_SIGNAL_RUN_LAST,
+                                          G_STRUCT_OFFSET (GVolumeMonitorClass, drive_connected),
+                                          NULL, NULL,
+                                          g_cclosure_marshal_VOID__OBJECT,
+                                          G_TYPE_NONE, 1, G_TYPE_DRIVE);
+  
+  
+  signals[DRIVE_DISCONNECTED] = g_signal_new (I_("drive_disconnected"),
+                                             G_TYPE_VOLUME_MONITOR,
+                                             G_SIGNAL_RUN_LAST,
+                                             G_STRUCT_OFFSET (GVolumeMonitorClass, drive_disconnected),
+                                             NULL, NULL,
+                                             g_cclosure_marshal_VOID__OBJECT,
+                                             G_TYPE_NONE, 1, G_TYPE_DRIVE);
+}
+
+static void
+g_volume_monitor_init (GVolumeMonitor *monitor)
+{
+}
+
+/**
+ * g_volume_monitor_get_mounted_volumes:
+ * @volume_monitor: a #GVolumeMonitor.
+ * 
+ * Returns a #GList of mounted #GVolumes.
+ * 
+ **/
+GList *
+g_volume_monitor_get_mounted_volumes  (GVolumeMonitor *volume_monitor)
+{
+  GVolumeMonitorClass *class;
+
+  g_return_val_if_fail (G_IS_VOLUME_MONITOR (volume_monitor), NULL);
+
+  class = G_VOLUME_MONITOR_GET_CLASS (volume_monitor);
+
+  return class->get_mounted_volumes (volume_monitor);
+}
+
+/**
+ * g_volume_monitor_get_connected_drives:
+ * @volume_monitor: a #GVolumeMonitor.
+ * 
+ * Returns a #GList of connected #GDrives. 
+ * 
+ **/
+GList *
+g_volume_monitor_get_connected_drives (GVolumeMonitor *volume_monitor)
+{
+  GVolumeMonitorClass *class;
+
+  g_return_val_if_fail (G_IS_VOLUME_MONITOR (volume_monitor), NULL);
+
+  class = G_VOLUME_MONITOR_GET_CLASS (volume_monitor);
+
+  return class->get_connected_drives (volume_monitor);
+}
+
diff --git a/gio/gvolumemonitor.h b/gio/gvolumemonitor.h
new file mode 100644 (file)
index 0000000..ef990a1
--- /dev/null
@@ -0,0 +1,88 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VOLUME_MONITOR_H__
+#define __G_VOLUME_MONITOR_H__
+
+#include <glib-object.h>
+#include <gio/gvolume.h>
+#include <gio/gdrive.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_VOLUME_MONITOR         (g_volume_monitor_get_type ())
+#define G_VOLUME_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_VOLUME_MONITOR, GVolumeMonitor))
+#define G_VOLUME_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_VOLUME_MONITOR, GVolumeMonitorClass))
+#define G_VOLUME_MONITOR_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_VOLUME_MONITOR, GVolumeMonitorClass))
+#define G_IS_VOLUME_MONITOR(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_VOLUME_MONITOR))
+#define G_IS_VOLUME_MONITOR_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_VOLUME_MONITOR))
+
+typedef struct _GVolumeMonitor GVolumeMonitor;
+typedef struct _GVolumeMonitorClass GVolumeMonitorClass;
+
+struct _GVolumeMonitor {
+  GObject parent;
+  gpointer priv;
+};
+
+struct _GVolumeMonitorClass {
+  GObjectClass parent_class;
+
+  /*< public >*/
+  /* signals */
+  void (* volume_mounted)      (GVolumeMonitor *volume_monitor,
+                                GVolume        *volume);
+  void (* volume_pre_unmount)  (GVolumeMonitor *volume_monitor,
+                                GVolume        *volume);
+  void (* volume_unmounted)    (GVolumeMonitor *volume_monitor,
+                                GVolume        *volume);
+  void (* drive_connected)     (GVolumeMonitor *volume_monitor,
+                                GDrive         *drive);
+  void (* drive_disconnected)  (GVolumeMonitor *volume_monitor,
+                                GDrive         *drive);
+
+  /* Vtable */
+
+  GList * (*get_mounted_volumes)  (GVolumeMonitor *volume_monitor);
+  GList * (*get_connected_drives) (GVolumeMonitor *volume_monitor);
+
+
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+};
+
+GType g_volume_monitor_get_type (void) G_GNUC_CONST;
+
+GVolumeMonitor *g_volume_monitor_get                  (void);
+GList *         g_volume_monitor_get_mounted_volumes  (GVolumeMonitor *volume_monitor);
+GList *         g_volume_monitor_get_connected_drives (GVolumeMonitor *volume_monitor);
+
+G_END_DECLS
+
+#endif /* __G_VOLUME_MONITOR_H__ */
diff --git a/gio/gvolumeprivate.h b/gio/gvolumeprivate.h
new file mode 100644 (file)
index 0000000..ce959de
--- /dev/null
@@ -0,0 +1,34 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_VOLUMEPRIV_H__
+#define __G_VOLUMEPRIV_H__
+
+#include <gio/gvolume.h>
+
+G_BEGIN_DECLS
+
+GVolume *g_volume_get_for_mount_path (const char *mountpoint);
+
+G_END_DECLS
+
+#endif /* __G_VOLUMEPRIV_H__ */
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
new file mode 100644 (file)
index 0000000..946b559
--- /dev/null
@@ -0,0 +1,672 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+
+#include "gcontenttypeprivate.h"
+#include "gwin32appinfo.h"
+#include "gioerror.h"
+#include <glib/gstdio.h>
+#include "glibintl.h"
+
+#include <windows.h>
+#include <shlwapi.h>
+
+#ifndef ASSOCF_INIT_BYEXENAME
+#define ASSOCF_INIT_BYEXENAME 0x00000002
+#endif
+
+/* These were wrong in MingW */
+#define REAL_ASSOCSTR_COMMAND 1
+#define REAL_ASSOCSTR_EXECUTABLE 2
+#define REAL_ASSOCSTR_FRIENDLYDOCNAME 3
+#define REAL_ASSOCSTR_FRIENDLYAPPNAME 4
+
+
+static void g_win32_app_info_iface_init (GAppInfoIface *iface);
+
+struct _GWin32AppInfo
+{
+  GObject parent_instance;
+  wchar_t *id;
+  char *id_utf8;
+  gboolean id_is_exename;
+  char *executable;
+  char *name;
+  gboolean no_open_with;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GWin32AppInfo, g_win32_app_info, G_TYPE_OBJECT,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_APP_INFO,
+                                               g_win32_app_info_iface_init))
+
+
+static void
+g_win32_app_info_finalize (GObject *object)
+{
+  GWin32AppInfo *info;
+
+  info = G_WIN32_APP_INFO (object);
+
+  g_free (info->id);
+  g_free (info->id_utf8);
+  g_free (info->name);
+  g_free (info->executable);
+  
+  if (G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_win32_app_info_parent_class)->finalize) (object);
+}
+
+static void
+g_win32_app_info_class_init (GWin32AppInfoClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+  
+  gobject_class->finalize = g_win32_app_info_finalize;
+}
+
+static void
+g_win32_app_info_init (GWin32AppInfo *local)
+{
+}
+
+static GAppInfo *
+g_desktop_app_info_new_from_id (wchar_t *id /* takes ownership */,
+                               gboolean id_is_exename)
+{
+  ASSOCF flags;
+  wchar_t buffer[1024];
+  DWORD buffer_size;
+  GWin32AppInfo *info;
+  HKEY app_key;
+  
+  info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
+  info->id = id; /* Takes ownership */
+  info->id_utf8 = g_utf16_to_utf8 (id, -1, NULL, NULL, NULL);  
+  info->id_is_exename = id_is_exename;
+
+  flags = 0;
+  if (id_is_exename)
+    flags |= ASSOCF_INIT_BYEXENAME;
+
+  buffer_size = 1024;
+  if (AssocQueryStringW(flags,
+                       REAL_ASSOCSTR_EXECUTABLE,
+                       id,
+                       NULL,
+                       buffer,
+                       &buffer_size) == S_OK)
+    info->executable = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+  buffer_size = 1024;
+  if (AssocQueryStringW(flags,
+                       REAL_ASSOCSTR_FRIENDLYAPPNAME,
+                       id,
+                       NULL,
+                       buffer,
+                       &buffer_size) == S_OK)
+    info->name = g_utf16_to_utf8 (buffer, -1, NULL, NULL, NULL);
+
+  if (info->name == NULL)
+    {
+      /* TODO: Should look up name from executable resources */
+      if (info->executable)
+       info->name = g_path_get_basename (info->executable);
+      else
+       info->name = g_strdup (info->id_utf8);
+    }
+
+  if (AssocQueryKeyW(flags,
+                    ASSOCKEY_APP,
+                    info->id,
+                    NULL,
+                    &app_key) == S_OK)
+    {
+      if (RegQueryValueExW (app_key, L"NoOpenWith", 0,
+                           NULL, NULL, NULL) == ERROR_SUCCESS)
+       info->no_open_with = TRUE;
+      RegCloseKey (app_key);
+    }
+  
+  return G_APP_INFO (info);
+}
+
+static wchar_t *
+dup_wstring (wchar_t *str)
+{
+  gsize len;
+  for (len = 0; str[len] != 0; len++)
+    ;
+  return (wchar_t *)g_memdup (str, (len + 1) * 2);
+}
+
+static GAppInfo *
+g_win32_app_info_dup (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+  GWin32AppInfo *new_info;
+  
+  new_info = g_object_new (G_TYPE_WIN32_APP_INFO, NULL);
+
+  new_info->id = dup_wstring (info->id);
+  new_info->id_utf8 = g_strdup (info->id_utf8);
+  new_info->id_is_exename = info->id_is_exename;
+  new_info->name = g_strdup (info->name);
+  new_info->executable = g_strdup (info->executable);
+  new_info->no_open_with = info->no_open_with;
+  
+  return G_APP_INFO (new_info);
+}
+
+static gboolean
+g_win32_app_info_equal (GAppInfo *appinfo1,
+                         GAppInfo *appinfo2)
+{
+  GWin32AppInfo *info1 = G_WIN32_APP_INFO (appinfo1);
+  GWin32AppInfo *info2 = G_WIN32_APP_INFO (appinfo2);
+
+  if (info1->executable == NULL ||
+      info2->executable == NULL)
+    return FALSE;
+  
+  return strcmp (info1->executable, info2->executable) == 0;
+}
+
+static const char *
+g_win32_app_info_get_id (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  return info->id_utf8;
+}
+
+static const char *
+g_win32_app_info_get_name (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  if (info->name == NULL)
+    return _("Unnamed");
+  
+  return info->name;
+}
+
+static const char *
+g_win32_app_info_get_description (GAppInfo *appinfo)
+{
+  /* Win32 has no app descriptions */
+  return NULL;
+}
+
+static const char *
+g_win32_app_info_get_executable (GAppInfo *appinfo)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+  
+  return info->executable;
+}
+
+static const char *
+g_win32_app_info_get_icon (GAppInfo *appinfo)
+{
+  /* GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo); */
+
+  /* TODO: How to handle icons */
+  return NULL;
+}
+
+static gboolean
+g_win32_app_info_launch (GAppInfo                *appinfo,
+                        GList                   *files,
+                        GAppLaunchContext       *launch_context,
+                        GError                 **error)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+  ASSOCF flags;
+  HKEY class_key;
+  SHELLEXECUTEINFOW exec_info = {0};
+  GList *l;
+
+  /* TODO:  What might startup_id mean on win32? */
+  
+  flags = 0;
+  if (info->id_is_exename)
+    flags |= ASSOCF_INIT_BYEXENAME;
+
+  if (AssocQueryKeyW(flags,
+                    ASSOCKEY_SHELLEXECCLASS,
+                    info->id,
+                    NULL,
+                    &class_key) != S_OK)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Can't find application"));
+      return FALSE;
+    }
+
+  for (l = file; l != NULL; l = l->next)
+    {
+      char *path = g_file_get_path (l->data);
+      wchar_t *wfilename = g_utf8_to_utf16 (path, -1, NULL, NULL, NULL);
+
+      g_free (path);
+      
+      memset (&exec_info, 0, sizeof (exec_info));
+      exec_info.cbSize = sizeof (exec_info);
+      exec_info.fMask = SEE_MASK_FLAG_DDEWAIT | SEE_MASK_CLASSKEY;
+      exec_info.lpFile = wfilename;     
+      exec_info.nShow = SW_SHOWNORMAL;
+      exec_info.hkeyClass = class_key;
+      
+      if (!ShellExecuteExW(&exec_info))
+       {
+         DWORD last_error;
+         LPVOID message;
+         char *message_utf8;
+         
+         last_error = GetLastError ();
+         FormatMessage (FORMAT_MESSAGE_ALLOCATE_BUFFER | 
+                        FORMAT_MESSAGE_FROM_SYSTEM,
+                        NULL,
+                        last_error,
+                        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                        (LPTSTR) &message,
+                        0, NULL );
+         
+         message_utf8 = g_utf16_to_utf8 (message, -1, NULL, NULL, NULL);
+         g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, _("Error launching application: %s"), message_utf8);
+         g_free (message_utf8);
+         LocalFree (message);
+         
+         g_free (wfilename);
+         RegCloseKey (class_key);
+         return FALSE;
+       }
+      
+      g_free (wfilename);
+    }
+  
+  RegCloseKey (class_key);
+  
+  return TRUE;
+}
+
+static gboolean
+g_win32_app_info_supports_uris (GAppInfo *appinfo)
+{
+  return FALSE;
+}
+
+static gboolean
+g_win32_app_info_launch_uris (GAppInfo *appinfo,
+                             GList *uris,
+                             GAppLaunchContext *launch_context,
+                             GError **error)
+{
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("URIs not supported"));
+  return FALSE;
+}
+
+static gboolean
+g_win32_app_info_should_show (GAppInfo *appinfo,
+                             const char *win32_env)
+{
+  GWin32AppInfo *info = G_WIN32_APP_INFO (appinfo);
+
+  if (info->no_open_with)
+    return FALSE;
+  
+  return TRUE;
+}
+
+static gboolean
+g_win32_app_info_set_as_default_for_type (GAppInfo    *appinfo,
+                                           const char  *content_type,
+                                           GError     **error)
+{
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("association changes not supported on win32"));
+  return FALSE;
+}
+
+GAppInfo *
+g_app_info_create_from_commandline (const char *commandline,
+                                   const char *application_name,
+                                   GAppInfoCreateFlags flags,
+                                   GError **error)
+{
+  g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Association creation not supported on win32"));
+  return NULL;
+}
+
+
+static void
+g_win32_app_info_iface_init (GAppInfoIface *iface)
+{
+  iface->dup = g_win32_app_info_dup;
+  iface->equal = g_win32_app_info_equal;
+  iface->get_id = g_win32_app_info_get_id;
+  iface->get_name = g_win32_app_info_get_name;
+  iface->get_description = g_win32_app_info_get_description;
+  iface->get_executable = g_win32_app_info_get_executable;
+  iface->get_icon = g_win32_app_info_get_icon;
+  iface->launch = g_win32_app_info_launch;
+  iface->supports_uris = g_win32_app_info_supports_uris;
+  iface->launch_uris = g_win32_app_info_launch_uris;
+  iface->should_show = g_win32_app_info_should_show;
+  iface->set_as_default_for_type = g_win32_app_info_set_as_default_for_type;
+}
+
+static void
+enumerate_open_with_list (HKEY dir_key,
+                         GList **prognames)
+{
+  DWORD index;
+  wchar_t name[256];
+  DWORD name_len, nbytes;
+  wchar_t data[256];
+  wchar_t *data_alloc;
+  DWORD type;
+
+  /* Must also look inside for a,b,c, + MRUList */
+  index = 0;
+  name_len = 256;
+  nbytes = sizeof (data) - 2;
+  while (RegEnumValueW(dir_key,
+                      index,
+                      name,
+                      &name_len,
+                      0,
+                      &type,
+                      (LPBYTE)data,
+                      &nbytes) == ERROR_SUCCESS)
+    {
+      data[nbytes/2] = '\0';
+      if (type == REG_SZ &&
+         /* Ignore things like MRUList, just look at 'a', 'b', 'c', etc */
+         name_len == 1)
+       {
+         data_alloc = (wchar_t *)g_memdup (data, nbytes + 2);
+         data_alloc[nbytes/2] = 0;
+         *prognames = g_list_prepend (*prognames, data_alloc);
+       }
+      index++;
+      name_len = 256;
+      nbytes = sizeof (data) - 2;
+    }
+  
+  index = 0;
+  name_len = 256;
+  while (RegEnumKeyExW(dir_key,
+                      index,
+                      name,
+                      &name_len,
+                      NULL,
+                      NULL,
+                      NULL,
+                      NULL) == ERROR_SUCCESS)
+    {
+      *prognames = g_list_prepend (*prognames, g_memdup (name, (name_len + 1) * 2));
+      index++;
+      name_len = 256;
+    }
+}
+
+static void
+enumerate_open_with_progids (HKEY dir_key,
+                            GList **progids)
+{
+  DWORD index;
+  wchar_t name[256];
+  DWORD name_len, type;
+
+  index = 0;
+  name_len = 256;
+  while (RegEnumValueW(dir_key,
+                      index,
+                      name,
+                      &name_len,
+                      0,
+                      &type,
+                      NULL,
+                      0) == ERROR_SUCCESS)
+    {
+      *progids = g_list_prepend (*progids, g_memdup (name, (name_len + 1) * 2));
+      index++;
+      name_len = 256;
+    }
+}
+
+static void
+enumerate_open_with_root (HKEY dir_key,
+                         GList **progids,
+                         GList **prognames)
+{
+  HKEY reg_key = NULL;
+  
+  if (RegOpenKeyExW (dir_key, L"OpenWithList", 0,
+                    KEY_READ, &reg_key) == ERROR_SUCCESS)
+    {
+      enumerate_open_with_list (reg_key, prognames);
+      RegCloseKey (reg_key);
+    }
+  
+  if (RegOpenKeyExW (dir_key, L"OpenWithProgids", 0,
+                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
+    {
+      enumerate_open_with_progids (reg_key, progids);
+      RegCloseKey (reg_key);
+    }
+}
+
+static gboolean
+app_info_in_list (GAppInfo *info, GList *l)
+{
+  while (l != NULL)
+    {
+      if (g_app_info_equal (info, l->data))
+       return TRUE;
+      l = l->next;
+    }
+  return FALSE;
+}
+
+/**
+ * g_app_info_get_all_for_type:
+ * @content_type:
+ * 
+ * Returns a #GList of #GAppInfo for a given @content_type.
+ * 
+ **/
+GList *
+g_app_info_get_all_for_type (const char *content_type)
+{
+  GList *progids = NULL;
+  GList *prognames = NULL;
+  HKEY reg_key, sys_file_assoc_key, reg_key2;
+  wchar_t percieved_type[128];
+  DWORD nchars, key_type;
+  wchar_t *wc_key;
+  GList *l;
+  GList *infos;
+
+  wc_key = g_utf8_to_utf16 (content_type, -1, NULL, NULL, NULL);
+  if (RegOpenKeyExW (HKEY_CLASSES_ROOT, wc_key, 0,
+                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
+    {
+      enumerate_open_with_root (reg_key, &progids, &prognames);
+
+      nchars = sizeof (percieved_type) / sizeof(wchar_t);
+      if (RegQueryValueExW (reg_key, L"PerceivedType", 0,
+                           &key_type, (LPBYTE) percieved_type, &nchars) == ERROR_SUCCESS)
+       {
+         if (key_type == REG_SZ &&
+             RegOpenKeyExW (HKEY_CLASSES_ROOT, L"SystemFileAssociations", 0,
+                            KEY_QUERY_VALUE, &sys_file_assoc_key) == ERROR_SUCCESS)
+           {
+             if (RegOpenKeyExW (sys_file_assoc_key, percieved_type, 0,
+                                KEY_QUERY_VALUE, &reg_key2) == ERROR_SUCCESS)
+               {
+                 enumerate_open_with_root (reg_key2, &progids, &prognames);
+                 RegCloseKey (reg_key2);
+               }
+
+             RegCloseKey (sys_file_assoc_key);
+           }
+       }
+      RegCloseKey (reg_key);
+    }
+
+  if (RegOpenKeyExW (HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts", 0,
+                    KEY_QUERY_VALUE, &reg_key) == ERROR_SUCCESS)
+    {
+      if (RegOpenKeyExW (reg_key, wc_key, 0,
+                        KEY_QUERY_VALUE, &reg_key2) == ERROR_SUCCESS)
+       {
+         enumerate_open_with_root (reg_key2, &progids, &prognames);
+         RegCloseKey (reg_key2);
+       }
+      
+      RegCloseKey (reg_key);
+    }
+
+  infos = NULL;
+  for (l = prognames; l != NULL; l = l->next)
+    {
+      GAppInfo *info;
+
+      /* l->data ownership is taken */
+      info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, TRUE);
+      if (app_info_in_list (info, infos))
+       g_object_unref (info);
+      else
+       infos = g_list_prepend (infos, info);
+    }
+  g_list_free (prognames);
+
+  for (l = progids; l != NULL; l = l->next)
+    {
+      GAppInfo *info;
+
+      /* l->data ownership is taken */
+      info = g_desktop_app_info_new_from_id ((wchar_t *)l->data, FALSE);
+      if (app_info_in_list (info, infos))
+       g_object_unref (info);
+      else
+       infos = g_list_prepend (infos, info);
+    }
+  g_list_free (progids);
+  
+  g_free (wc_key);
+  return g_list_reverse (infos);
+}
+
+/**
+ * g_app_info_get_default_for_type:
+ * @content_type:
+ * @must_support_uris:
+ * 
+ * Returns the default #GAppInfo for the given @content_type. If 
+ * @must_support_uris is true, the #GAppInfo is expected to support
+ * URIs. 
+ * 
+ **/
+GAppInfo *
+g_app_info_get_default_for_type (const char *content_type,
+                                gboolean must_support_uris)
+{
+  wchar_t *wtype;
+  wchar_t buffer[1024];
+  DWORD buffer_size;
+
+  wtype = g_utf8_to_utf16 (content_type, -1, NULL, NULL, NULL);
+
+  /* Verify that we have some sort of app registered for this type */
+  buffer_size = 1024;
+  if (AssocQueryStringW(0,
+                       REAL_ASSOCSTR_COMMAND,
+                       wtype,
+                       NULL,
+                       buffer,
+                       &buffer_size) == S_OK)
+    /* Takes ownership of wtype */
+    return g_desktop_app_info_new_from_id (wtype, FALSE);
+
+  g_free (wtype);
+  return NULL;
+}
+
+/**
+ * g_app_info_get_default_for_uri_scheme:
+ * @uri_scheme:
+ * 
+ **/
+GAppInfo *
+g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
+{
+  /* TODO: Implement */
+  return NULL;
+}
+
+/**
+ * g_app_info_get_all:
+ * 
+ **/
+GList *
+g_app_info_get_all (void)
+{
+  DWORD index;
+  wchar_t name[256];
+  DWORD name_len;
+  HKEY reg_key;
+  GList *infos;
+  GAppInfo *info;
+
+  if (RegOpenKeyExW (HKEY_CLASSES_ROOT, L"Applications", 0,
+                    KEY_READ, &reg_key) != ERROR_SUCCESS)
+    return NULL;
+
+  infos = NULL;
+  index = 0;
+  name_len = 256;
+  while (RegEnumKeyExW(reg_key,
+                      index,
+                      name,
+                      &name_len,
+                      NULL,
+                      NULL,
+                      NULL,
+                      NULL) == ERROR_SUCCESS)
+    {
+      wchar_t *name_dup = g_memdup (name, (name_len+1)*2);
+      /* name_dup ownership is taken */
+      info = g_desktop_app_info_new_from_id (name_dup, TRUE);
+      infos = g_list_prepend (infos, info);
+      
+      index++;
+      name_len = 256;
+    }
+  
+  RegCloseKey (reg_key);
+
+  return g_list_reverse (infos);
+}
diff --git a/gio/gwin32appinfo.h b/gio/gwin32appinfo.h
new file mode 100644 (file)
index 0000000..cfec62f
--- /dev/null
@@ -0,0 +1,50 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl@redhat.com>
+ */
+
+#ifndef __G_WIN32_APP_INFO_H__
+#define __G_WIN32_APP_INFO_H__
+
+#include <gio/gappinfo.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_WIN32_APP_INFO         (g_win32_app_info_get_type ())
+#define G_WIN32_APP_INFO(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_WIN32_APP_INFO, GWin32AppInfo))
+#define G_WIN32_APP_INFO_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_WIN32_APP_INFO, GWin32AppInfoClass))
+#define G_IS_WIN32_APP_INFO(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_WIN32_APP_INFO))
+#define G_IS_WIN32_APP_INFO_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_WIN32_APP_INFO))
+#define G_WIN32_APP_INFO_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_WIN32_APP_INFO, GWin32AppInfoClass))
+
+typedef struct _GWin32AppInfo        GWin32AppInfo;
+typedef struct _GWin32AppInfoClass   GWin32AppInfoClass;
+
+struct _GWin32AppInfoClass
+{
+  GObjectClass parent_class;
+};
+
+GType g_win32_app_info_get_type (void) G_GNUC_CONST;
+
+G_END_DECLS
+
+
+#endif /* __G_WIN32_APP_INFO_H__ */
diff --git a/gio/inotify/Makefile.am b/gio/inotify/Makefile.am
new file mode 100644 (file)
index 0000000..191f27d
--- /dev/null
@@ -0,0 +1,34 @@
+NULL =
+
+noinst_LTLIBRARIES = libinotify.la
+
+libinotify_la_SOURCES =                \
+       inotify-kernel.c                \
+       inotify-sub.c                   \
+       inotify-path.c                  \
+       inotify-missing.c               \
+       inotify-helper.c                \
+       inotify-diag.c                  \
+       inotify-diag.h                  \
+       inotify-kernel.h                \
+       inotify-missing.h               \
+       inotify-path.h                  \
+       inotify-sub.h                   \
+       inotify-helper.h                \
+       local_inotify.h                 \
+       local_inotify_syscalls.h        \
+       ginotifyfilemonitor.c           \
+       ginotifyfilemonitor.h           \
+       ginotifydirectorymonitor.c      \
+       ginotifydirectorymonitor.h      \
+       $(NULL)
+
+libinotify_la_CFLAGS = \
+       -DG_LOG_DOMAIN=\"GLib-GIO\"     \
+       -I$(top_srcdir)                 \
+       -I$(top_srcdir)/glib            \
+       -I$(top_srcdir)/gmodule         \
+       -I$(top_srcdir)/gio             \
+       -DGIO_MODULE_DIR=\"$(libdir)/gio/modules\"  \
+       -DG_DISABLE_DEPRECATED
+
diff --git a/gio/inotify/ginotifydirectorymonitor.c b/gio/inotify/ginotifydirectorymonitor.c
new file mode 100644 (file)
index 0000000..bec3609
--- /dev/null
@@ -0,0 +1,144 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#include <config.h>
+
+#include "ginotifydirectorymonitor.h"
+#include "giomodule.h"
+
+#define USE_INOTIFY 1
+#include "inotify-helper.h"
+
+struct _GInotifyDirectoryMonitor
+{
+  GLocalDirectoryMonitor parent_instance;
+  inotify_sub *sub;
+};
+
+static gboolean g_inotify_directory_monitor_cancel (GDirectoryMonitor* monitor);
+
+G_DEFINE_TYPE (GInotifyDirectoryMonitor, g_inotify_directory_monitor, G_TYPE_LOCAL_DIRECTORY_MONITOR)
+
+static void
+g_inotify_directory_monitor_finalize (GObject *object)
+{
+  GInotifyDirectoryMonitor *inotify_monitor = G_INOTIFY_DIRECTORY_MONITOR (object);
+  inotify_sub *sub = inotify_monitor->sub;
+
+  if (inotify_monitor->sub)
+    {
+      _ih_sub_cancel (sub);
+      _ih_sub_free (sub);
+      inotify_monitor->sub = NULL;
+    }
+
+  if (G_OBJECT_CLASS (g_inotify_directory_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_inotify_directory_monitor_parent_class)->finalize) (object);
+}
+
+static GObject *
+g_inotify_directory_monitor_constructor (GType type,
+                                    guint n_construct_properties,
+                                    GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GInotifyDirectoryMonitorClass *klass;
+  GObjectClass *parent_class;
+  GInotifyDirectoryMonitor *inotify_monitor;
+  const gchar *dirname = NULL;
+  inotify_sub *sub = NULL;
+  
+  klass = G_INOTIFY_DIRECTORY_MONITOR_CLASS (g_type_class_peek (G_TYPE_INOTIFY_DIRECTORY_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  inotify_monitor = G_INOTIFY_DIRECTORY_MONITOR (obj);
+
+  dirname = G_LOCAL_DIRECTORY_MONITOR (obj)->dirname;
+  g_assert (dirname != NULL);
+
+  /* Will never fail as is_supported() should be called before instanciating
+   * anyway */
+  g_assert (_ih_startup ());
+
+  sub = _ih_sub_new (dirname, NULL, inotify_monitor);
+  /* FIXME: what to do about errors here? we can't return NULL or another
+   * kind of error and an assertion is probably too hard */
+  g_assert (sub != NULL);
+  g_assert (_ih_sub_add (sub));
+
+  inotify_monitor->sub = sub;
+
+  return obj;
+}
+
+static gboolean
+g_inotify_directory_monitor_is_supported (void)
+{
+  return _ih_startup ();
+}
+
+static void
+g_inotify_directory_monitor_class_init (GInotifyDirectoryMonitorClass* klass)
+{
+  GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+  GDirectoryMonitorClass *directory_monitor_class = G_DIRECTORY_MONITOR_CLASS (klass);
+  GLocalDirectoryMonitorClass *local_directory_monitor_class = G_LOCAL_DIRECTORY_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_inotify_directory_monitor_finalize;
+  gobject_class->constructor = g_inotify_directory_monitor_constructor;
+  directory_monitor_class->cancel = g_inotify_directory_monitor_cancel;
+
+  local_directory_monitor_class->prio = 20;
+  local_directory_monitor_class->mount_notify = TRUE;
+  local_directory_monitor_class->is_supported = g_inotify_directory_monitor_is_supported;
+}
+
+static void
+g_inotify_directory_monitor_init (GInotifyDirectoryMonitor* monitor)
+{
+
+}
+
+static gboolean
+g_inotify_directory_monitor_cancel (GDirectoryMonitor* monitor)
+{
+  GInotifyDirectoryMonitor *inotify_monitor = G_INOTIFY_DIRECTORY_MONITOR (monitor);
+  inotify_sub *sub = inotify_monitor->sub;
+
+  if (sub) {
+    _ih_sub_cancel (sub);
+    _ih_sub_free (sub);
+    inotify_monitor->sub = NULL;
+  }
+
+  if (G_DIRECTORY_MONITOR_CLASS (g_inotify_directory_monitor_parent_class)->cancel)
+    (*G_DIRECTORY_MONITOR_CLASS (g_inotify_directory_monitor_parent_class)->cancel) (monitor);
+
+  return TRUE;
+}
+
diff --git a/gio/inotify/ginotifydirectorymonitor.h b/gio/inotify/ginotifydirectorymonitor.h
new file mode 100644 (file)
index 0000000..bc17098
--- /dev/null
@@ -0,0 +1,54 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#ifndef __G_INOTIFY_DIRECTORY_MONITOR_H__
+#define __G_INOTIFY_DIRECTORY_MONITOR_H__
+
+#include <glib-object.h>
+#include <string.h>
+#include <gio/gdirectorymonitor.h>
+#include "glocaldirectorymonitor.h"
+#include "giomodule.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_INOTIFY_DIRECTORY_MONITOR               (g_inotify_directory_monitor_get_type ())
+#define G_INOTIFY_DIRECTORY_MONITOR(o)                 (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_INOTIFY_DIRECTORY_MONITOR, GInotifyDirectoryMonitor))
+#define G_INOTIFY_DIRECTORY_MONITOR_CLASS(k)           (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_INOTIFY_DIRECTORY_MONITOR, GInotifyDirectoryMonitorClass))
+#define G_IS_INOTIFY_DIRECTORY_MONITOR(o)              (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_INOTIFY_DIRECTORY_MONITOR))
+#define G_IS_INOTIFY_DIRECTORY_MONITOR_CLASS(k)        (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_INOTIFY_DIRECTORY_MONITOR))
+
+typedef struct _GInotifyDirectoryMonitor      GInotifyDirectoryMonitor;
+typedef struct _GInotifyDirectoryMonitorClass GInotifyDirectoryMonitorClass;
+
+struct _GInotifyDirectoryMonitorClass {
+  GLocalDirectoryMonitorClass parent_class;
+};
+
+GType g_inotify_directory_monitor_get_type (void);
+
+G_END_DECLS
+
+#endif /* __G_INOTIFY_DIRECTORY_MONITOR_H__ */
diff --git a/gio/inotify/ginotifyfilemonitor.c b/gio/inotify/ginotifyfilemonitor.c
new file mode 100644 (file)
index 0000000..4132116
--- /dev/null
@@ -0,0 +1,162 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#include <config.h>
+
+#include "ginotifyfilemonitor.h"
+#include <gio/giomodule.h>
+
+#define USE_INOTIFY 1
+#include "inotify-helper.h"
+
+struct _GInotifyFileMonitor
+{
+  GLocalFileMonitor parent_instance;
+  gchar *filename;
+  gchar *dirname;
+  inotify_sub *sub;
+};
+
+static gboolean g_inotify_file_monitor_cancel (GFileMonitor* monitor);
+
+G_DEFINE_TYPE (GInotifyFileMonitor, g_inotify_file_monitor, G_TYPE_LOCAL_FILE_MONITOR)
+
+static void
+g_inotify_file_monitor_finalize (GObject *object)
+{
+  GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (object);
+  inotify_sub *sub = inotify_monitor->sub;
+
+  if (inotify_monitor->sub)
+    {
+      _ih_sub_cancel (sub);
+      _ih_sub_free (sub);
+      inotify_monitor->sub = NULL;
+    }
+
+  if (inotify_monitor->filename)
+    {
+      g_free (inotify_monitor->filename);
+      inotify_monitor->filename = NULL;
+    }
+
+  if (inotify_monitor->dirname)
+    {
+      g_free (inotify_monitor->dirname);
+      inotify_monitor->dirname = NULL;
+    }
+
+  if (G_OBJECT_CLASS (g_inotify_file_monitor_parent_class)->finalize)
+    (*G_OBJECT_CLASS (g_inotify_file_monitor_parent_class)->finalize) (object);
+}
+
+static GObject *
+g_inotify_file_monitor_constructor (GType type,
+                                    guint n_construct_properties,
+                                    GObjectConstructParam *construct_properties)
+{
+  GObject *obj;
+  GInotifyFileMonitorClass *klass;
+  GObjectClass *parent_class;
+  GInotifyFileMonitor *inotify_monitor;
+  const gchar *filename = NULL;
+  inotify_sub *sub = NULL;
+  
+  klass = G_INOTIFY_FILE_MONITOR_CLASS (g_type_class_peek (G_TYPE_INOTIFY_FILE_MONITOR));
+  parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
+  obj = parent_class->constructor (type,
+                                   n_construct_properties,
+                                   construct_properties);
+
+  inotify_monitor = G_INOTIFY_FILE_MONITOR (obj);
+
+  filename = G_LOCAL_FILE_MONITOR (obj)->filename;
+
+  g_assert (filename != NULL);
+
+  inotify_monitor->filename = g_path_get_basename (filename);
+  inotify_monitor->dirname = g_path_get_dirname (filename);
+
+  /* Will never fail as is_supported() should be called before instanciating
+   * anyway */
+  g_assert (_ih_startup ());
+
+  sub = _ih_sub_new (inotify_monitor->dirname, inotify_monitor->filename, inotify_monitor);
+  /* FIXME: what to do about errors here? we can't return NULL or another
+   * kind of error and an assertion is probably too hard */
+  g_assert (sub != NULL);
+  g_assert (_ih_sub_add (sub));
+
+  inotify_monitor->sub = sub;
+
+  return obj;
+}
+
+static gboolean
+g_inotify_file_monitor_is_supported (void)
+{
+  return _ih_startup ();
+}
+
+static void
+g_inotify_file_monitor_class_init (GInotifyFileMonitorClass* klass)
+{
+  GObjectClass* gobject_class = G_OBJECT_CLASS (klass);
+  GFileMonitorClass *file_monitor_class = G_FILE_MONITOR_CLASS (klass);
+  GLocalFileMonitorClass *local_file_monitor_class = G_LOCAL_FILE_MONITOR_CLASS (klass);
+  
+  gobject_class->finalize = g_inotify_file_monitor_finalize;
+  gobject_class->constructor = g_inotify_file_monitor_constructor;
+  file_monitor_class->cancel = g_inotify_file_monitor_cancel;
+
+  local_file_monitor_class->prio = 20;
+  local_file_monitor_class->is_supported = g_inotify_file_monitor_is_supported;
+}
+
+static void
+g_inotify_file_monitor_init (GInotifyFileMonitor* monitor)
+{
+
+}
+
+static gboolean
+g_inotify_file_monitor_cancel (GFileMonitor* monitor)
+{
+  GInotifyFileMonitor *inotify_monitor = G_INOTIFY_FILE_MONITOR (monitor);
+  inotify_sub *sub = inotify_monitor->sub;
+
+  if (sub) {
+    _ih_sub_cancel (sub);
+    _ih_sub_free (sub);
+    inotify_monitor->sub = NULL;
+  }
+
+  if (G_FILE_MONITOR_CLASS (g_inotify_file_monitor_parent_class)->cancel)
+    (*G_FILE_MONITOR_CLASS (g_inotify_file_monitor_parent_class)->cancel) (monitor);
+
+  return TRUE;
+}
+
diff --git a/gio/inotify/ginotifyfilemonitor.h b/gio/inotify/ginotifyfilemonitor.h
new file mode 100644 (file)
index 0000000..ae877bc
--- /dev/null
@@ -0,0 +1,54 @@
+/* GIO - GLib Input, Output and Streaming Library
+ * 
+ * Copyright (C) 2006-2007 Red Hat, Inc.
+ * Copyright (C) 2007 Sebastian Dröge.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Authors: Alexander Larsson <alexl@redhat.com>
+ *          John McCutchan <john@johnmccutchan.com> 
+ *          Sebastian Dröge <slomo@circular-chaos.org>
+ */
+
+#ifndef __G_INOTIFY_FILE_MONITOR_H__
+#define __G_INOTIFY_FILE_MONITOR_H__
+
+#include <glib-object.h>
+#include <string.h>
+#include <gio/gfilemonitor.h>
+#include <gio/glocalfilemonitor.h>
+#include <gio/giomodule.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_INOTIFY_FILE_MONITOR            (g_inotify_file_monitor_get_type ())
+#define G_INOTIFY_FILE_MONITOR(o)                      (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_INOTIFY_FILE_MONITOR, GInotifyFileMonitor))
+#define G_INOTIFY_FILE_MONITOR_CLASS(k)                (G_TYPE_CHECK_CLASS_CAST ((k), G_TYPE_INOTIFY_FILE_MONITOR, GInotifyFileMonitorClass))
+#define G_IS_INOTIFY_FILE_MONITOR(o)           (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_INOTIFY_FILE_MONITOR))
+#define G_IS_INOTIFY_FILE_MONITOR_CLASS(k)     (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_INOTIFY_FILE_MONITOR))
+
+typedef struct _GInotifyFileMonitor      GInotifyFileMonitor;
+typedef struct _GInotifyFileMonitorClass GInotifyFileMonitorClass;
+
+struct _GInotifyFileMonitorClass {
+  GLocalFileMonitorClass parent_class;
+};
+
+GType g_inotify_file_monitor_get_type (void);
+
+G_END_DECLS
+
+#endif /* __G_INOTIFY_FILE_MONITOR_H__ */
diff --git a/gio/inotify/inotify-diag.c b/gio/inotify/inotify-diag.c
new file mode 100644 (file)
index 0000000..937ebd7
--- /dev/null
@@ -0,0 +1,74 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+
+/* inotify-helper.c - Gnome VFS Monitor based on inotify.
+
+   Copyright (C) 2005 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: 
+                John McCutchan <john@johnmccutchan.com>
+*/
+
+#include "config.h"
+#include <glib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include "inotify-missing.h"
+#include "inotify-path.h"
+#include "inotify-diag.h"
+
+#define DIAG_DUMP_TIME 20000 /* 20 seconds */
+
+G_LOCK_EXTERN (inotify_lock);
+
+static gboolean
+id_dump (gpointer userdata)
+{
+  GIOChannel *ioc;
+  pid_t pid;
+  char *fname;
+  G_LOCK (inotify_lock);
+  ioc = NULL;
+  pid = getpid ();
+
+  fname = g_strdup_printf ("/tmp/gvfsid.%d", pid);
+  ioc = g_io_channel_new_file (fname, "w", NULL);
+  g_free (fname);
+  
+  if (!ioc)
+    {
+      G_UNLOCK (inotify_lock);
+      return TRUE;
+    }
+
+  _im_diag_dump (ioc);
+  
+  g_io_channel_shutdown (ioc, TRUE, NULL);
+  g_io_channel_unref (ioc);
+  
+  G_UNLOCK (inotify_lock);
+  return TRUE;
+}
+
+void
+_id_startup (void)
+{
+  if (!g_getenv ("GVFS_INOTIFY_DIAG"))
+    return;
+       
+  g_timeout_add (DIAG_DUMP_TIME, id_dump, NULL);
+}
diff --git a/gio/inotify/inotify-diag.h b/gio/inotify/inotify-diag.h
new file mode 100644 (file)
index 0000000..f818f16
--- /dev/null
@@ -0,0 +1,29 @@
+/* inotify-helper.h - GNOME VFS Monitor using inotify
+
+   Copyright (C) 2006 John McCutchan <john@johnmccutchan.com>
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: John McCutchan <john@johnmccutchan.com>
+*/
+
+
+#ifndef __INOTIFY_DIAG_H
+#define __INOTIFY_DIAG_H
+
+void _id_startup (void);
+
+#endif /* __INOTIFY_DIAG_H */
diff --git a/gio/inotify/inotify-helper.c b/gio/inotify/inotify-helper.c
new file mode 100644 (file)
index 0000000..82a68de
--- /dev/null
@@ -0,0 +1,264 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+
+/* inotify-helper.c - GVFS Monitor based on inotify.
+
+   Copyright (C) 2007 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: 
+                John McCutchan <john@johnmccutchan.com>
+*/
+
+#include "config.h"
+#include <errno.h>
+#include <time.h>
+#include <string.h>
+#include <sys/ioctl.h>
+/* Just include the local header to stop all the pain */
+#include "local_inotify.h"
+#if 0
+#ifdef HAVE_SYS_INOTIFY_H
+/* We don't actually include the libc header, because there has been
+ * problems with libc versions that was built without inotify support.
+ * Instead we use the local version.
+ */
+#include "local_inotify.h"
+#elif defined (HAVE_LINUX_INOTIFY_H)
+#include <linux/inotify.h>
+#endif
+#endif
+#include <gio/glocalfile.h>
+#include <gio/gfilemonitor.h>
+#include <gio/gdirectorymonitor.h>
+#include "inotify-helper.h"
+#include "inotify-missing.h"
+#include "inotify-path.h"
+#include "inotify-diag.h"
+
+static gboolean ih_debug_enabled = FALSE;
+#define IH_W if (ih_debug_enabled) g_warning 
+
+static void ih_event_callback (ik_event_t *event, inotify_sub *sub);
+static void ih_not_missing_callback (inotify_sub *sub);
+
+/* We share this lock with inotify-kernel.c and inotify-missing.c
+ *
+ * inotify-kernel.c takes the lock when it reads events from
+ * the kernel and when it processes those events
+ *
+ * inotify-missing.c takes the lock when it is scanning the missing
+ * list.
+ *
+ * We take the lock in all public functions
+ */
+G_LOCK_DEFINE (inotify_lock);
+
+static GFileMonitorEvent ih_mask_to_EventFlags (guint32 mask);
+
+/**
+ * _ih_startup:
+ *
+ * Initializes the inotify backend.  This must be called before
+ * any other functions in this module.
+ *
+ * Return value: #TRUE if initialization succeeded, #FALSE otherwise
+ */
+gboolean
+_ih_startup (void)
+{
+  static gboolean initialized = FALSE;
+  static gboolean result = FALSE;
+  
+  G_LOCK (inotify_lock);
+  
+  if (initialized == TRUE)
+    {
+      G_UNLOCK (inotify_lock);
+      return result;
+    }
+
+  result = _ip_startup (ih_event_callback);
+  if (!result)
+    {
+      g_warning ("Could not initialize inotify\n");
+      G_UNLOCK (inotify_lock);
+      return FALSE;
+    }
+  _im_startup (ih_not_missing_callback);
+  _id_startup ();
+
+  IH_W ("started gvfs inotify backend\n");
+  
+  initialized = TRUE;
+  
+  G_UNLOCK (inotify_lock);
+  
+  return TRUE;
+}
+
+/**
+ * Adds a subscription to be monitored.
+ */
+gboolean
+_ih_sub_add (inotify_sub * sub)
+{
+  G_LOCK (inotify_lock);
+       
+  if (!_ip_start_watching (sub))
+    _im_add (sub);
+  
+  G_UNLOCK (inotify_lock);
+  return TRUE;
+}
+
+/**
+ * Cancels a subscription which was being monitored.
+ */
+gboolean
+_ih_sub_cancel (inotify_sub * sub)
+{
+  G_LOCK (inotify_lock);
+
+  if (!sub->cancelled)
+    {
+      IH_W ("cancelling %s\n", sub->dirname);
+      sub->cancelled = TRUE;
+      _im_rm (sub);
+      _ip_stop_watching (sub);
+    }
+  
+  G_UNLOCK (inotify_lock);
+  
+  return TRUE;
+}
+
+
+static void
+ih_event_callback (ik_event_t *event, inotify_sub *sub)
+{
+  gchar *fullpath;
+  GFileMonitorEvent eflags;
+  GFile* parent;
+  GFile* child;
+  
+  eflags = ih_mask_to_EventFlags (event->mask);
+  parent = g_file_new_for_path (sub->dirname);
+  if (event->name)
+    fullpath = g_strdup_printf ("%s/%s", sub->dirname, event->name);
+  else
+    fullpath = g_strdup_printf ("%s/", sub->dirname);
+  
+  child = g_file_new_for_path (fullpath);
+  g_free (fullpath);
+
+  if (G_IS_DIRECTORY_MONITOR (sub->user_data))
+    {
+      GDirectoryMonitor* monitor = G_DIRECTORY_MONITOR (sub->user_data);
+      g_directory_monitor_emit_event (monitor, 
+                                     child, NULL, eflags);
+    }
+  else if (G_IS_FILE_MONITOR (sub->user_data))
+    {
+      GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
+      g_file_monitor_emit_event (monitor,
+                                child, NULL, eflags);
+    }
+
+  g_object_unref (child);
+  g_object_unref (parent);
+}
+
+static void
+ih_not_missing_callback (inotify_sub *sub)
+{
+  gchar *fullpath;
+  GFileMonitorEvent eflags;
+  guint32 mask;
+  GFile* parent;
+  GFile* child;
+  
+  parent = g_file_new_for_path (sub->dirname);
+
+  if (sub->filename)
+    {
+      fullpath = g_strdup_printf ("%s/%s", sub->dirname, sub->filename);
+      g_warning ("Missing callback called fullpath = %s\n", fullpath);
+      if (!g_file_test (fullpath, G_FILE_TEST_EXISTS))
+       {
+         g_free (fullpath);
+         return;
+       }
+      mask = IN_CREATE;
+    }
+  else
+    {
+      fullpath = g_strdup_printf ("%s", sub->dirname);
+      mask = IN_CREATE|IN_ISDIR;
+    }
+
+  eflags = ih_mask_to_EventFlags (mask);
+  child = g_file_new_for_path (fullpath);
+  g_free (fullpath);
+
+  if (G_IS_DIRECTORY_MONITOR (sub->user_data))
+    {
+      GDirectoryMonitor* monitor = G_DIRECTORY_MONITOR (sub->user_data);
+      g_directory_monitor_emit_event (monitor, child, NULL, eflags);
+    }
+  else if (G_IS_FILE_MONITOR (sub->user_data))
+    {
+      GFileMonitor* monitor = G_FILE_MONITOR (sub->user_data);
+      g_file_monitor_emit_event (monitor,
+                                child, NULL, eflags);
+    }
+
+  g_object_unref (child);
+  g_object_unref (parent);
+}
+
+/* Transforms a inotify event to a GVFS event. */
+static GFileMonitorEvent
+ih_mask_to_EventFlags (guint32 mask)
+{
+  mask &= ~IN_ISDIR;
+  switch (mask)
+    {
+    case IN_MODIFY:
+      return G_FILE_MONITOR_EVENT_CHANGED;
+    case IN_CLOSE_WRITE:
+      return G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT;
+    case IN_ATTRIB:
+      return G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED;
+    case IN_MOVE_SELF:
+    case IN_MOVED_FROM:
+    case IN_DELETE:
+    case IN_DELETE_SELF:
+      return G_FILE_MONITOR_EVENT_DELETED;
+    case IN_CREATE:
+    case IN_MOVED_TO:
+      return G_FILE_MONITOR_EVENT_CREATED;
+    case IN_UNMOUNT:
+      return G_FILE_MONITOR_EVENT_UNMOUNTED;
+    case IN_Q_OVERFLOW:
+    case IN_OPEN:
+    case IN_CLOSE_NOWRITE:
+    case IN_ACCESS:
+    case IN_IGNORED:
+    default:
+      return -1;
+    }
+}
diff --git a/gio/inotify/inotify-helper.h b/gio/inotify/inotify-helper.h
new file mode 100644 (file)
index 0000000..1fd9701
--- /dev/null
@@ -0,0 +1,33 @@
+/* inotify-helper.h - GVFS Directory Monitor using inotify
+
+   Copyright (C) 2007 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: John McCutchan <john@johnmccutchan.com>
+*/
+
+
+#ifndef __INOTIFY_HELPER_H
+#define __INOTIFY_HELPER_H
+
+#include "inotify-sub.h"
+
+gboolean _ih_startup    (void);
+gboolean _ih_sub_add    (inotify_sub *sub);
+gboolean _ih_sub_cancel (inotify_sub *sub);
+
+#endif /* __INOTIFY_HELPER_H */
diff --git a/gio/inotify/inotify-kernel.c b/gio/inotify/inotify-kernel.c
new file mode 100644 (file)
index 0000000..6735c45
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+   Copyright (C) 2005 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors:.
+               John McCutchan <john@johnmccutchan.com>
+*/
+
+#include "config.h"
+
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <glib.h>
+#include "inotify-kernel.h"
+
+/* Just include the local headers to stop all the pain */
+#include "local_inotify.h"
+#include "local_inotify_syscalls.h"
+#if 0
+#ifdef HAVE_SYS_INOTIFY_H
+/* We don't actually include the libc header, because there has been
+ * problems with libc versions that was built without inotify support.
+ * Instead we use the local version.
+ */
+#include "local_inotify.h"
+#include "local_inotify_syscalls.h"
+#elif defined (HAVE_LINUX_INOTIFY_H)
+#include <linux/inotify.h>
+#include "local_inotify_syscalls.h"
+#endif
+#endif
+
+/* Timings for pairing MOVED_TO / MOVED_FROM events */
+#define PROCESS_EVENTS_TIME 1000 /* milliseconds (1 hz) */
+#define DEFAULT_HOLD_UNTIL_TIME 0 /* 0 millisecond */
+#define MOVE_HOLD_UNTIL_TIME 0 /* 0 milliseconds */
+
+static int inotify_instance_fd = -1;
+static GQueue *events_to_process = NULL;
+static GQueue *event_queue = NULL;
+static GHashTable * cookie_hash = NULL;
+static GIOChannel *inotify_read_ioc;
+static GPollFD ik_poll_fd;
+static gboolean ik_poll_fd_enabled = TRUE;
+static void (*user_cb)(ik_event_t *event);
+
+static gboolean ik_read_callback (gpointer user_data);
+static gboolean ik_process_eq_callback (gpointer user_data);
+
+static guint32 ik_move_matches = 0;
+static guint32 ik_move_misses = 0;
+
+static gboolean process_eq_running = FALSE;
+
+/* We use the lock from inotify-helper.c
+ *
+ * There are two places that we take this lock
+ *
+ * 1) In ik_read_callback
+ *
+ * 2) ik_process_eq_callback.
+ *
+ *
+ * The rest of locking is taken care of in inotify-helper.c
+ */
+G_LOCK_EXTERN (inotify_lock);
+
+typedef struct ik_event_internal {
+  ik_event_t *event;
+  gboolean seen;
+  gboolean sent;
+  GTimeVal hold_until;
+  struct ik_event_internal *pair;
+} ik_event_internal_t;
+
+/* In order to perform non-sleeping inotify event chunking we need
+ * a custom GSource
+ */
+static gboolean
+ik_source_prepare (GSource *source,
+                  gint *timeout)
+{
+  return FALSE;
+}
+
+static gboolean
+ik_source_timeout (gpointer data)
+{
+  GSource *source = (GSource *)data;
+  
+  /* Re-active the PollFD */
+  g_source_add_poll (source, &ik_poll_fd);
+  g_source_unref (source);
+  ik_poll_fd_enabled = TRUE;
+  
+  return FALSE;
+}
+
+#define MAX_PENDING_COUNT 2
+#define PENDING_THRESHOLD(qsize) ((qsize) >> 1)
+#define PENDING_MARGINAL_COST(p) ((unsigned int)(1 << (p)))
+#define MAX_QUEUED_EVENTS 2048
+#define AVERAGE_EVENT_SIZE sizeof (struct inotify_event) + 16
+#define TIMEOUT_MILLISECONDS 10
+
+static gboolean
+ik_source_check (GSource *source)
+{
+  static int prev_pending = 0, pending_count = 0;
+  
+  /* We already disabled the PollFD or
+   * nothing to be read from inotify */
+  if (!ik_poll_fd_enabled || !(ik_poll_fd.revents & G_IO_IN))
+    return FALSE;
+
+  if (pending_count < MAX_PENDING_COUNT)
+    {
+      unsigned int pending;
+      
+      if (ioctl (inotify_instance_fd, FIONREAD, &pending) == -1)
+       goto do_read;
+      
+      pending /= AVERAGE_EVENT_SIZE;
+      
+      /* Don't wait if the number of pending events is too close
+       * to the maximum queue size.
+       */
+      if (pending > PENDING_THRESHOLD (MAX_QUEUED_EVENTS))
+       goto do_read;
+      
+      /* With each successive iteration, the minimum rate for
+       * further sleep doubles. */
+      if (pending-prev_pending < PENDING_MARGINAL_COST (pending_count))
+       goto do_read;
+      
+      prev_pending = pending;
+      pending_count++;
+      
+      /* We are going to wait to read the events: */
+      
+      /* Remove the PollFD from the source */
+      g_source_remove_poll (source, &ik_poll_fd);
+      /* To avoid threading issues we need to flag that we've done that */
+      ik_poll_fd_enabled = FALSE;
+      /* Set a timeout to re-add the PollFD to the source */
+      g_source_ref (source);
+      g_timeout_add (TIMEOUT_MILLISECONDS, ik_source_timeout, source);
+      
+      return FALSE;
+    }
+
+do_read:
+  /* We are ready to read events from inotify */
+
+  prev_pending = 0;
+  pending_count = 0;
+  
+  return TRUE;
+}
+
+static gboolean
+ik_source_dispatch (GSource *source,
+                   GSourceFunc callback,
+                   gpointer user_data)
+{
+  if (callback)
+    return callback (user_data);
+  return TRUE;
+}
+
+static GSourceFuncs ik_source_funcs =
+{
+  ik_source_prepare,
+  ik_source_check,
+  ik_source_dispatch,
+  NULL
+};
+
+gboolean _ik_startup (void (*cb)(ik_event_t *event))
+{
+  static gboolean initialized = FALSE;
+  GSource *source;
+  
+  user_cb = cb;
+  /* Ignore multi-calls */
+  if (initialized) 
+    return inotify_instance_fd >= 0;
+  
+  initialized = TRUE;
+  inotify_instance_fd = inotify_init ();
+  
+  if (inotify_instance_fd < 0)
+    return FALSE;
+
+  inotify_read_ioc = g_io_channel_unix_new (inotify_instance_fd);
+  ik_poll_fd.fd = inotify_instance_fd;
+  ik_poll_fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+  g_io_channel_set_encoding (inotify_read_ioc, NULL, NULL);
+  g_io_channel_set_flags (inotify_read_ioc, G_IO_FLAG_NONBLOCK, NULL);
+
+  source = g_source_new (&ik_source_funcs, sizeof(GSource));
+  g_source_add_poll (source, &ik_poll_fd);
+  g_source_set_callback (source, ik_read_callback, NULL, NULL);
+  g_source_attach (source, NULL);
+  g_source_unref (source);
+
+  cookie_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+  event_queue = g_queue_new ();
+  events_to_process = g_queue_new ();
+  
+  return TRUE;
+}
+
+static ik_event_internal_t *
+ik_event_internal_new (ik_event_t *event)
+{
+  ik_event_internal_t *internal_event = g_new0 (ik_event_internal_t, 1);
+  GTimeVal tv;
+  
+  g_assert (event);
+  
+  g_get_current_time (&tv);
+  g_time_val_add (&tv, DEFAULT_HOLD_UNTIL_TIME);
+  internal_event->event = event;
+  internal_event->hold_until = tv;
+  
+  return internal_event;
+}
+
+static ik_event_t *
+ik_event_new (char *buffer)
+{
+  struct inotify_event *kevent = (struct inotify_event *)buffer;
+  ik_event_t *event = g_new0(ik_event_t,1);
+  
+  g_assert (buffer);
+  
+  event->wd = kevent->wd;
+  event->mask = kevent->mask;
+  event->cookie = kevent->cookie;
+  event->len = kevent->len;
+  if (event->len)
+    event->name = g_strdup (kevent->name);
+  else
+    event->name = g_strdup ("");
+  
+  return event;
+}
+
+ik_event_t *
+_ik_event_new_dummy (const char *name, gint32 wd, guint32 mask)
+{
+  ik_event_t *event = g_new0 (ik_event_t,1);
+  event->wd = wd;
+  event->mask = mask;
+  event->cookie = 0;
+  if (name)
+    event->name = g_strdup (name);
+  else
+    event->name = g_strdup("");
+  
+  event->len = strlen (event->name);
+  
+  return event;
+}
+
+void
+_ik_event_free (ik_event_t *event)
+{
+  if (event->pair)
+    _ik_event_free (event->pair);
+  g_free (event->name);
+  g_free (event);
+}
+
+gint32
+_ik_watch (const char *path, guint32 mask, int *err)
+{
+  gint32 wd = -1;
+  
+  g_assert (path != NULL);
+  g_assert (inotify_instance_fd >= 0);
+  
+  wd = inotify_add_watch (inotify_instance_fd, path, mask);
+  
+  if (wd < 0)
+    {
+      int e = errno;
+      /* FIXME: debug msg failed to add watch */
+      if (err)
+       *err = e;
+      return wd;
+    }
+  
+  g_assert (wd >= 0);
+  return wd;
+}
+
+int
+_ik_ignore(const char *path, gint32 wd)
+{
+  g_assert (wd >= 0);
+  g_assert (inotify_instance_fd >= 0);
+  
+  if (inotify_rm_watch (inotify_instance_fd, wd) < 0)
+    {
+      /* int e = errno; */
+      /* failed to rm watch */
+      return -1;
+    }
+  
+  return 0;
+}
+
+void
+_ik_move_stats (guint32 *matches, guint32 *misses)
+{
+  if (matches)
+    *matches = ik_move_matches;
+  
+  if (misses)
+    *misses = ik_move_misses;
+}
+
+const char *
+_ik_mask_to_string (guint32 mask)
+{
+  gboolean is_dir = mask & IN_ISDIR;
+  mask &= ~IN_ISDIR;
+  
+  if (is_dir)
+    {
+      switch (mask)
+       {
+       case IN_ACCESS:
+         return "ACCESS (dir)";
+       case IN_MODIFY:
+         return "MODIFY (dir)";
+       case IN_ATTRIB:
+         return "ATTRIB (dir)";
+       case IN_CLOSE_WRITE:
+         return "CLOSE_WRITE (dir)";
+       case IN_CLOSE_NOWRITE:
+         return "CLOSE_NOWRITE (dir)"; 
+       case IN_OPEN:
+         return "OPEN (dir)";
+       case IN_MOVED_FROM:
+         return "MOVED_FROM (dir)";
+       case IN_MOVED_TO:
+         return "MOVED_TO (dir)";
+       case IN_DELETE:
+         return "DELETE (dir)";
+       case IN_CREATE:
+         return "CREATE (dir)";
+       case IN_DELETE_SELF:
+         return "DELETE_SELF (dir)";
+       case IN_UNMOUNT:
+         return "UNMOUNT (dir)";
+       case IN_Q_OVERFLOW:
+         return "Q_OVERFLOW (dir)";
+       case IN_IGNORED:
+         return "IGNORED (dir)";
+       default:
+         return "UNKNOWN_EVENT (dir)";
+       }
+    }
+  else
+    {
+      switch (mask)
+       {
+       case IN_ACCESS:
+         return "ACCESS";
+       case IN_MODIFY:
+         return "MODIFY";
+       case IN_ATTRIB:
+         return "ATTRIB";
+       case IN_CLOSE_WRITE:
+         return "CLOSE_WRITE";
+       case IN_CLOSE_NOWRITE:
+         return "CLOSE_NOWRITE";
+       case IN_OPEN:
+         return "OPEN";
+       case IN_MOVED_FROM:
+         return "MOVED_FROM";
+       case IN_MOVED_TO:
+         return "MOVED_TO";
+       case IN_DELETE:
+         return "DELETE";
+       case IN_CREATE:
+         return "CREATE";
+       case IN_DELETE_SELF:
+         return "DELETE_SELF";
+       case IN_UNMOUNT:
+         return "UNMOUNT";
+       case IN_Q_OVERFLOW:
+         return "Q_OVERFLOW";
+       case IN_IGNORED:
+         return "IGNORED";
+       default:
+         return "UNKNOWN_EVENT";
+       }
+    }
+}
+
+
+static void
+ik_read_events (gsize *buffer_size_out, gchar **buffer_out)
+{
+  static gchar *buffer = NULL;
+  static gsize buffer_size;
+  
+  /* Initialize the buffer on our first call */
+  if (buffer == NULL)
+    {
+      buffer_size = AVERAGE_EVENT_SIZE;
+      buffer_size *= MAX_QUEUED_EVENTS;
+      buffer = g_malloc (buffer_size);
+    }
+
+  *buffer_size_out = 0;
+  *buffer_out = NULL;
+  
+  memset (buffer, 0, buffer_size);
+
+  if (g_io_channel_read_chars (inotify_read_ioc, (char *)buffer, buffer_size, buffer_size_out, NULL) != G_IO_STATUS_NORMAL) {
+    /* error reading */
+  }
+  *buffer_out = buffer;
+}
+
+static gboolean
+ik_read_callback (gpointer user_data)
+{
+  gchar *buffer;
+  gsize buffer_size, buffer_i, events;
+  
+  G_LOCK (inotify_lock);
+  ik_read_events (&buffer_size, &buffer);
+  
+  buffer_i = 0;
+  events = 0;
+  while (buffer_i < buffer_size)
+    {
+      struct inotify_event *event;
+      gsize event_size;
+      event = (struct inotify_event *)&buffer[buffer_i];
+      event_size = sizeof(struct inotify_event) + event->len;
+      g_queue_push_tail (events_to_process, ik_event_internal_new (ik_event_new (&buffer[buffer_i])));
+      buffer_i += event_size;
+      events++;
+    }
+  
+  /* If the event process callback is off, turn it back on */
+  if (!process_eq_running && events)
+    {
+      process_eq_running = TRUE;
+      g_timeout_add (PROCESS_EVENTS_TIME, ik_process_eq_callback, NULL);
+    }
+  
+  G_UNLOCK (inotify_lock);
+  
+  return TRUE;
+}
+
+static gboolean
+g_timeval_lt (GTimeVal *val1, GTimeVal *val2)
+{
+  if (val1->tv_sec < val2->tv_sec)
+    return TRUE;
+  
+  if (val1->tv_sec > val2->tv_sec)
+    return FALSE;
+  
+  /* val1->tv_sec == val2->tv_sec */
+  if (val1->tv_usec < val2->tv_usec)
+    return TRUE;
+  
+  return FALSE;
+}
+
+static gboolean
+g_timeval_eq (GTimeVal *val1, GTimeVal *val2)
+{
+  return (val1->tv_sec == val2->tv_sec) && (val1->tv_usec == val2->tv_usec);
+}
+
+static void
+ik_pair_events (ik_event_internal_t *event1, ik_event_internal_t *event2)
+{
+  g_assert (event1 && event2);
+  /* We should only be pairing events that have the same cookie */
+  g_assert (event1->event->cookie == event2->event->cookie);
+  /* We shouldn't pair an event that already is paired */
+  g_assert (event1->pair == NULL && event2->pair == NULL);
+  
+  /* Pair the internal structures and the ik_event_t structures */
+  event1->pair = event2;
+  event1->event->pair = event2->event;
+  
+  if (g_timeval_lt (&event1->hold_until, &event2->hold_until))
+    event1->hold_until = event2->hold_until;
+  
+  event2->hold_until = event1->hold_until;
+}
+
+static void
+ik_event_add_microseconds (ik_event_internal_t *event, glong ms)
+{
+  g_assert (event);
+  g_time_val_add (&event->hold_until, ms);
+}
+
+static gboolean
+ik_event_ready (ik_event_internal_t *event)
+{
+  GTimeVal tv;
+  g_assert (event);
+  
+  g_get_current_time (&tv);
+  
+  /* An event is ready if,
+   *
+   * it has no cookie -- there is nothing to be gained by holding it
+   * or, it is already paired -- we don't need to hold it anymore
+   * or, we have held it long enough
+   */
+  return
+    event->event->cookie == 0 ||
+    event->pair != NULL ||
+    g_timeval_lt (&event->hold_until, &tv) ||
+    g_timeval_eq (&event->hold_until, &tv);
+}
+
+static void
+ik_pair_moves (gpointer data, gpointer user_data)
+{
+  ik_event_internal_t *event = (ik_event_internal_t *)data;
+  
+  if (event->seen == TRUE || event->sent == TRUE)
+    return;
+  
+  if (event->event->cookie != 0)
+    {
+      /* When we get a MOVED_FROM event we delay sending the event by
+       * MOVE_HOLD_UNTIL_TIME microseconds. We need to do this because a
+       * MOVED_TO pair _might_ be coming in the near future */
+      if (event->event->mask & IN_MOVED_FROM)
+       {
+         g_hash_table_insert (cookie_hash, GINT_TO_POINTER (event->event->cookie), event);
+         /* because we don't deliver move events there is no point in waiting for the match right now. */
+         ik_event_add_microseconds (event, MOVE_HOLD_UNTIL_TIME);
+       }
+      else if (event->event->mask & IN_MOVED_TO)
+       {
+         /* We need to check if we are waiting for this MOVED_TO events cookie to pair it with
+          * a MOVED_FROM */
+         ik_event_internal_t *match = NULL;
+         match = g_hash_table_lookup (cookie_hash, GINT_TO_POINTER (event->event->cookie));
+         if (match)
+           {
+             g_hash_table_remove (cookie_hash, GINT_TO_POINTER (event->event->cookie));
+             ik_pair_events (match, event);
+           }
+       }
+    }
+  event->seen = TRUE;
+}
+
+static void
+ik_process_events (void)
+{
+  g_queue_foreach (events_to_process, ik_pair_moves, NULL);
+
+  while (!g_queue_is_empty (events_to_process))
+    {
+      ik_event_internal_t *event = g_queue_peek_head (events_to_process);
+      
+      /* This must have been sent as part of a MOVED_TO/MOVED_FROM */
+      if (event->sent)
+       {
+         /* Pop event */
+         g_queue_pop_head (events_to_process);
+         /* Free the internal event structure */
+         g_free (event);
+         continue;
+       }
+      
+      /* The event isn't ready yet */
+      if (!ik_event_ready (event))
+       break;
+      
+      /* Pop it */
+      event = g_queue_pop_head (events_to_process);
+      
+      /* Check if this is a MOVED_FROM that is also sitting in the cookie_hash */
+      if (event->event->cookie && event->pair == NULL &&
+         g_hash_table_lookup (cookie_hash, GINT_TO_POINTER (event->event->cookie)))
+       g_hash_table_remove (cookie_hash, GINT_TO_POINTER (event->event->cookie));
+      
+      if (event->pair)
+       {
+         /* We send out paired MOVED_FROM/MOVED_TO events in the same event buffer */
+         /* g_assert (event->event->mask == IN_MOVED_FROM && event->pair->event->mask == IN_MOVED_TO); */
+         /* Copy the paired data */
+         event->pair->sent = TRUE;
+         event->sent = TRUE;
+         ik_move_matches++;
+       }
+      else if (event->event->cookie)
+       {
+         /* If we couldn't pair a MOVED_FROM and MOVED_TO together, we change
+          * the event masks */
+         /* Changeing MOVED_FROM to DELETE and MOVED_TO to create lets us make
+          * the gaurantee that you will never see a non-matched MOVE event */
+         
+         if (event->event->mask & IN_MOVED_FROM)
+           {
+             event->event->mask = IN_DELETE|(event->event->mask & IN_ISDIR);
+             ik_move_misses++; /* not super accurate, if we aren't watching the destination it still counts as a miss */
+           }
+         if (event->event->mask & IN_MOVED_TO)
+           event->event->mask = IN_CREATE|(event->event->mask & IN_ISDIR);
+       }
+      
+      /* Push the ik_event_t onto the event queue */
+      g_queue_push_tail (event_queue, event->event);
+      /* Free the internal event structure */
+      g_free (event);
+    }
+}
+
+static gboolean
+ik_process_eq_callback (gpointer user_data)
+{
+  gboolean res;
+  
+  /* Try and move as many events to the event queue */
+  G_LOCK (inotify_lock);
+  ik_process_events ();
+  
+  while (!g_queue_is_empty (event_queue))
+    {
+      ik_event_t *event = g_queue_pop_head (event_queue);
+      
+      user_cb (event);
+    }
+
+  res = TRUE;
+  
+  if (g_queue_get_length (events_to_process) == 0)
+    {
+      process_eq_running = FALSE;
+      res = FALSE;
+    }
+  
+  G_UNLOCK (inotify_lock);
+  
+  return res;
+}
diff --git a/gio/inotify/inotify-kernel.h b/gio/inotify/inotify-kernel.h
new file mode 100644 (file)
index 0000000..b406d71
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+   Copyright (C) 2005 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors:.
+               John McCutchan <john@johnmccutchan.com>
+*/
+
+#ifndef __INOTIFY_KERNEL_H
+#define __INOTIFY_KERNEL_H
+
+typedef struct ik_event_s {
+  gint32 wd;
+  guint32 mask;
+  guint32 cookie;
+  guint32 len;
+  char *  name;
+  struct ik_event_s *pair;
+} ik_event_t;
+
+gboolean _ik_startup (void (*cb) (ik_event_t *event));
+
+ik_event_t *_ik_event_new_dummy (const char *name,
+                                gint32      wd,
+                                guint32     mask);
+void        _ik_event_free      (ik_event_t *event);
+
+gint32      _ik_watch           (const char *path,
+                                guint32     mask,
+                                int        *err);
+int         _ik_ignore          (const char *path,
+                                gint32      wd);
+
+
+/* The miss count will probably be enflated */
+void        _ik_move_stats     (guint32 *matches,
+                               guint32 *misses);
+const char *_ik_mask_to_string (guint32  mask);
+
+
+#endif
diff --git a/gio/inotify/inotify-missing.c b/gio/inotify/inotify-missing.c
new file mode 100644 (file)
index 0000000..96126b3
--- /dev/null
@@ -0,0 +1,167 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+
+/* inotify-helper.c - Gnome VFS Monitor based on inotify.
+
+   Copyright (C) 2005 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: 
+                John McCutchan <john@johnmccutchan.com>
+*/
+
+#include "config.h"
+#include <glib.h>
+#include "inotify-missing.h"
+#include "inotify-path.h"
+
+#define SCAN_MISSING_TIME 4000 /* 1/4 Hz */
+
+static gboolean im_debug_enabled = FALSE;
+#define IM_W if (im_debug_enabled) g_warning
+
+/* We put inotify_sub's that are missing on this list */
+static GList *missing_sub_list = NULL;
+static gboolean im_scan_missing (gpointer user_data);
+static gboolean scan_missing_running = FALSE;
+static void (*missing_cb)(inotify_sub *sub) = NULL;
+
+G_LOCK_EXTERN (inotify_lock);
+
+/* inotify_lock must be held before calling */
+void
+_im_startup (void (*callback)(inotify_sub *sub))
+{
+  static gboolean initialized = FALSE;
+  
+  if (!initialized)
+    {
+      missing_cb = callback;
+      initialized = TRUE;
+    }
+}
+
+/* inotify_lock must be held before calling */
+void
+_im_add (inotify_sub *sub)
+{
+  if (g_list_find (missing_sub_list, sub))
+    {
+      IM_W ("asked to add %s to missing list but it's already on the list!\n", sub->dirname);
+      return;
+    }
+
+  IM_W ("adding %s to missing list\n", sub->dirname);
+  missing_sub_list = g_list_prepend (missing_sub_list, sub);
+
+  /* If the timeout is turned off, we turn it back on */
+  if (!scan_missing_running)
+    {
+      scan_missing_running = TRUE;
+      g_timeout_add (SCAN_MISSING_TIME, im_scan_missing, NULL);
+    }
+}
+
+/* inotify_lock must be held before calling */
+void
+_im_rm (inotify_sub *sub)
+{
+  GList *link;
+  
+  link = g_list_find (missing_sub_list, sub);
+
+  if (!link)
+    {
+      IM_W ("asked to remove %s from missing list but it isn't on the list!\n", sub->dirname);
+      return;
+    }
+
+  IM_W ("removing %s from missing list\n", sub->dirname);
+
+  missing_sub_list = g_list_remove_link (missing_sub_list, link);
+  g_list_free_1 (link);
+}
+
+/* Scans the list of missing subscriptions checking if they
+ * are available yet.
+ */
+static gboolean
+im_scan_missing (gpointer user_data)
+{
+  GList *nolonger_missing = NULL;
+  GList *l;
+  
+  G_LOCK (inotify_lock);
+  
+  IM_W ("scanning missing list with %d items\n", g_list_length (missing_sub_list));
+  for (l = missing_sub_list; l; l = l->next)
+    {
+      inotify_sub *sub = l->data;
+      gboolean not_m = FALSE;
+      
+      IM_W ("checking %p\n", sub);
+      g_assert (sub);
+      g_assert (sub->dirname);
+      not_m = _ip_start_watching (sub);
+
+      if (not_m)
+       {
+         missing_cb (sub);
+         IM_W ("removed %s from missing list\n", sub->dirname);
+         /* We have to build a list of list nodes to remove from the
+          * missing_sub_list. We do the removal outside of this loop.
+          */
+         nolonger_missing = g_list_prepend (nolonger_missing, l);
+       } 
+    }
+
+  for (l = nolonger_missing; l ; l = l->next)
+    {
+      GList *llink = l->data;
+      missing_sub_list = g_list_remove_link (missing_sub_list, llink);
+      g_list_free_1 (llink);
+    }
+
+  g_list_free (nolonger_missing);
+  
+  /* If the missing list is now empty, we disable the timeout */
+  if (missing_sub_list == NULL)
+    {
+      scan_missing_running = FALSE;
+      G_UNLOCK (inotify_lock);
+      return FALSE;
+    }
+  else
+    {
+      G_UNLOCK (inotify_lock);
+      return TRUE;
+    }
+}
+
+
+/* inotify_lock must be held */
+void
+_im_diag_dump (GIOChannel *ioc)
+{
+  GList *l;
+  g_io_channel_write_chars (ioc, "missing list:\n", -1, NULL, NULL);
+  for (l = missing_sub_list; l; l = l->next)
+    {
+      inotify_sub *sub = l->data;
+      g_io_channel_write_chars (ioc, sub->dirname, -1, NULL, NULL);
+      g_io_channel_write_chars (ioc, "\n", -1, NULL, NULL);
+    }
+}
diff --git a/gio/inotify/inotify-missing.h b/gio/inotify/inotify-missing.h
new file mode 100644 (file)
index 0000000..b67b595
--- /dev/null
@@ -0,0 +1,35 @@
+/* inotify-helper.h - GNOME VFS Monitor using inotify
+
+   Copyright (C) 2006 John McCutchan <john@johnmccutchan.com>
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: John McCutchan <ttb@tentacle.dhs.org>
+*/
+
+
+#ifndef __INOTIFY_MISSING_H
+#define __INOTIFY_MISSING_H
+
+#include "inotify-sub.h"
+
+void _im_startup   (void (*missing_cb)(inotify_sub *sub));
+void _im_add       (inotify_sub *sub);
+void _im_rm        (inotify_sub *sub);
+void _im_diag_dump (GIOChannel  *ioc);
+
+
+#endif /* __INOTIFY_MISSING_H */
diff --git a/gio/inotify/inotify-path.c b/gio/inotify/inotify-path.c
new file mode 100644 (file)
index 0000000..76fc06d
--- /dev/null
@@ -0,0 +1,417 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+
+/* inotify-path.c - GVFS Directory Monitor based on inotify.
+
+   Copyright (C) 2006 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors:
+                John McCutchan <john@johnmccutchan.com>
+*/
+
+#include "config.h"
+
+/* Don't put conflicting kernel types in the global namespace: */
+#define __KERNEL_STRICT_NAMES
+
+#include "local_inotify.h"
+#if 0
+#ifdef HAVE_SYS_INOTIFY_H
+/* We don't actually include the libc header, because there has been
+ * problems with libc versions that was built without inotify support.
+ * Instead we use the local version.
+ */
+#elif defined (HAVE_LINUX_INOTIFY_H)
+#include <linux/inotify.h>
+#endif
+#endif
+#include <string.h>
+#include <glib.h>
+#include "inotify-kernel.h"
+#include "inotify-path.h"
+#include "inotify-missing.h"
+
+#define IP_INOTIFY_MASK (IN_MODIFY|IN_ATTRIB|IN_MOVED_FROM|IN_MOVED_TO|IN_DELETE|IN_CREATE|IN_DELETE_SELF|IN_UNMOUNT|IN_MOVE_SELF|IN_CLOSE_WRITE)
+
+typedef struct ip_watched_dir_s {
+  char *path;
+  /* TODO: We need to maintain a tree of watched directories
+   * so that we can deliver move/delete events to sub folders.
+   * Or the application could do it...
+   */
+  struct ip_watched_dir_s* parent;
+  GList*        children;
+
+  /* Inotify state */
+  gint32 wd;
+  
+  /* List of inotify subscriptions */
+  GList *subs;
+} ip_watched_dir_t;
+
+static gboolean     ip_debug_enabled = FALSE;
+#define IP_W if (ip_debug_enabled) g_warning
+
+/* path -> ip_watched_dir */
+static GHashTable * path_dir_hash = NULL;
+/* inotify_sub * -> ip_watched_dir *
+ *
+ * Each subscription is attached to a watched directory or it is on
+ * the missing list
+ */
+static GHashTable * sub_dir_hash = NULL;
+/* This hash holds GLists of ip_watched_dir_t *'s
+ * We need to hold a list because symbolic links can share
+ * the same wd
+ */
+static GHashTable * wd_dir_hash = NULL;
+
+static ip_watched_dir_t *ip_watched_dir_new  (const char       *path,
+                                             int               wd);
+static void              ip_watched_dir_free (ip_watched_dir_t *dir);
+static void              ip_event_callback   (ik_event_t       *event);
+
+
+static void (*event_callback)(ik_event_t *event, inotify_sub *sub);
+
+gboolean
+_ip_startup (void (*cb)(ik_event_t *event, inotify_sub *sub))
+{
+  static gboolean initialized = FALSE;
+  static gboolean result = FALSE;
+  
+  if (initialized == TRUE)
+    return result;
+
+  event_callback = cb;
+  result = _ik_startup (ip_event_callback);
+
+  if (!result)
+    return FALSE;
+
+  path_dir_hash = g_hash_table_new (g_str_hash, g_str_equal);
+  sub_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+  wd_dir_hash = g_hash_table_new (g_direct_hash, g_direct_equal);
+  
+  initialized = TRUE;
+  return TRUE;
+}
+
+static void
+ip_map_path_dir (const char *path, ip_watched_dir_t *dir)
+{
+  g_assert (path && dir);
+  g_hash_table_insert (path_dir_hash, dir->path, dir);
+}
+
+static void
+ip_map_sub_dir (inotify_sub *sub, ip_watched_dir_t *dir)
+{
+  /* Associate subscription and directory */
+  g_assert (dir && sub);
+  g_hash_table_insert (sub_dir_hash, sub, dir);
+  dir->subs = g_list_prepend (dir->subs, sub);
+}
+
+static void
+ip_map_wd_dir (gint32 wd, ip_watched_dir_t *dir)
+{
+  GList *dir_list;
+  
+  g_assert (wd >= 0 && dir);
+  dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
+  dir_list = g_list_prepend (dir_list, dir);
+  g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list);
+}
+
+gboolean
+_ip_start_watching (inotify_sub *sub)
+{
+  gint32 wd;
+  int err;
+  ip_watched_dir_t *dir;
+  
+  g_assert (sub);
+  g_assert (!sub->cancelled);
+  g_assert (sub->dirname);
+  
+  IP_W ("Starting to watch %s\n", sub->dirname);
+  dir = g_hash_table_lookup (path_dir_hash, sub->dirname);
+  if (dir)
+    {
+      IP_W ("Already watching\n");
+      goto out;
+    }
+       
+  IP_W ("Trying to add inotify watch ");
+  wd = _ik_watch (sub->dirname, IP_INOTIFY_MASK|IN_ONLYDIR, &err);
+  if (wd < 0) 
+    {
+      IP_W("Failed\n");
+      return FALSE;
+    }
+  else
+    {
+      /* Create new watched directory and associate it with the 
+       * wd hash and path hash
+       */
+      IP_W ("Success\n");
+      dir = ip_watched_dir_new (sub->dirname, wd);
+      ip_map_wd_dir (wd, dir);
+      ip_map_path_dir (sub->dirname, dir);
+    }
+  
+ out:
+  ip_map_sub_dir (sub, dir);
+  
+  return TRUE;
+}
+
+static void
+ip_unmap_path_dir (const char *path, ip_watched_dir_t *dir)
+{
+  g_assert (path && dir);
+  g_hash_table_remove (path_dir_hash, dir->path);
+}
+
+static void
+ip_unmap_wd_dir (gint32 wd, ip_watched_dir_t *dir)
+{
+  GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
+  
+  if (!dir_list)
+    return;
+  
+  g_assert (wd >= 0 && dir);
+  dir_list = g_list_remove (dir_list, dir);
+  if (dir_list == NULL) 
+    g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER (dir->wd));
+  else
+    g_hash_table_replace (wd_dir_hash, GINT_TO_POINTER (dir->wd), dir_list);
+}
+
+static void
+ip_unmap_wd (gint32 wd)
+{
+  GList *dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (wd));
+  if (!dir_list)
+    return;
+  g_assert (wd >= 0);
+  g_hash_table_remove (wd_dir_hash, GINT_TO_POINTER (wd));
+  g_list_free (dir_list);
+}
+
+static void
+ip_unmap_sub_dir (inotify_sub *sub, ip_watched_dir_t *dir)
+{
+  g_assert (sub && dir);
+  g_hash_table_remove (sub_dir_hash, sub);
+  dir->subs = g_list_remove (dir->subs, sub);
+}
+
+static void
+ip_unmap_all_subs (ip_watched_dir_t *dir)
+{
+  GList *l = NULL;
+  
+  for (l = dir->subs; l; l = l->next)
+    {
+      inotify_sub *sub = l->data;
+      g_hash_table_remove (sub_dir_hash, sub);
+    }
+  g_list_free (dir->subs);
+  dir->subs = NULL;
+}
+
+gboolean
+_ip_stop_watching  (inotify_sub *sub)
+{
+  ip_watched_dir_t *dir = NULL;
+  
+  dir = g_hash_table_lookup (sub_dir_hash, sub);
+  if (!dir) 
+    return TRUE;
+  
+  ip_unmap_sub_dir (sub, dir);
+  
+  /* No one is subscribing to this directory any more */
+  if (dir->subs == NULL)
+    {
+      _ik_ignore (dir->path, dir->wd);
+      ip_unmap_wd_dir (dir->wd, dir);
+      ip_unmap_path_dir (dir->path, dir);
+      ip_watched_dir_free (dir);
+    }
+  
+  return TRUE;
+}
+
+
+static ip_watched_dir_t *
+ip_watched_dir_new (const char *path, gint32 wd)
+{
+  ip_watched_dir_t *dir = g_new0 (ip_watched_dir_t, 1);
+  
+  dir->path = g_strdup (path);
+  dir->wd = wd;
+  
+  return dir;
+}
+
+static void
+ip_watched_dir_free (ip_watched_dir_t * dir)
+{
+  g_assert (dir->subs == NULL);
+  g_free (dir->path);
+  g_free (dir);
+}
+
+static void
+ip_wd_delete (gpointer data, gpointer user_data)
+{
+  ip_watched_dir_t *dir = data;
+  GList *l = NULL;
+  
+  for (l = dir->subs; l; l = l->next)
+    {
+      inotify_sub *sub = l->data;
+      /* Add subscription to missing list */
+      _im_add (sub);
+    }
+  ip_unmap_all_subs (dir);
+  /* Unassociate the path and the directory */
+  ip_unmap_path_dir (dir->path, dir);
+  ip_watched_dir_free (dir);
+}
+
+static void
+ip_event_dispatch (GList *dir_list, GList* pair_dir_list, ik_event_t *event)
+{
+  GList *dirl;
+  
+  if (!event)
+    return;
+
+  for (dirl = dir_list; dirl; dirl = dirl->next)
+    {
+      GList *subl;
+      ip_watched_dir_t *dir = dirl->data;
+      
+      for (subl = dir->subs; subl; subl = subl->next)
+       {
+         inotify_sub *sub = subl->data;
+         
+         /* If the subscription and the event
+          * contain a filename and they don't
+          * match, we don't deliver this event.
+          */
+         if (sub->filename &&
+             event->name &&
+             strcmp (sub->filename, event->name))
+           continue;
+         
+         /* If the subscription has a filename
+          * but this event doesn't, we don't
+          * deliever this event.
+          */
+         if (sub->filename && !event->name)
+           continue;
+         
+         /* FIXME: We might need to synthesize
+          * DELETE/UNMOUNT events when
+          * the filename doesn't match
+          */
+         
+         event_callback (event, sub);
+       }
+    }
+  
+  if (!event->pair)
+    return;
+  
+  for (dirl = pair_dir_list; dirl; dirl = dirl->next)
+    {
+      GList *subl;
+      ip_watched_dir_t *dir = dirl->data;
+      
+      for (subl = dir->subs; subl; subl = subl->next)
+       {
+         inotify_sub *sub = subl->data;
+         
+         /* If the subscription and the event
+          * contain a filename and they don't
+          * match, we don't deliver this event.
+          */
+         if (sub->filename &&
+             event->pair->name &&
+             strcmp (sub->filename, event->pair->name))
+           continue;
+         
+         /* If the subscription has a filename
+          * but this event doesn't, we don't
+          * deliever this event.
+          */
+         if (sub->filename && !event->pair->name)
+           continue;
+         
+         /* FIXME: We might need to synthesize
+          * DELETE/UNMOUNT events when
+          * the filename doesn't match
+          */
+         
+         event_callback (event->pair, sub);
+       }
+    }
+}
+
+static void
+ip_event_callback (ik_event_t *event)
+{
+  GList* dir_list = NULL;
+  GList* pair_dir_list = NULL;
+  
+  dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->wd));
+  
+  /* We can ignore the IGNORED events */
+  if (event->mask & IN_IGNORED)
+    {
+      _ik_event_free (event);
+      return;
+    }
+
+  if (event->pair)
+    pair_dir_list = g_hash_table_lookup (wd_dir_hash, GINT_TO_POINTER (event->pair->wd));
+
+  if (event->mask & IP_INOTIFY_MASK)
+    ip_event_dispatch (dir_list, pair_dir_list, event);
+  
+  /* We have to manage the missing list
+   * when we get an event that means the
+   * file has been deleted/moved/unmounted.
+   */
+  if (event->mask & IN_DELETE_SELF ||
+      event->mask & IN_MOVE_SELF ||
+      event->mask & IN_UNMOUNT)
+    {
+      /* Add all subscriptions to missing list */
+      g_list_foreach (dir_list, ip_wd_delete, NULL);
+      /* Unmap all directories attached to this wd */
+      ip_unmap_wd (event->wd);
+    }
+  
+  _ik_event_free (event);
+}
diff --git a/gio/inotify/inotify-path.h b/gio/inotify/inotify-path.h
new file mode 100644 (file)
index 0000000..c613b9f
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+   Copyright (C) 2005 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors:.
+               John McCutchan <john@johnmccutchan.com>
+*/
+
+#ifndef __INOTIFY_PATH_H
+#define __INOTIFY_PATH_H
+
+#include "inotify-kernel.h"
+#include "inotify-sub.h"
+
+gboolean _ip_startup (void (*event_cb)(ik_event_t *event, inotify_sub *sub));
+gboolean _ip_start_watching (inotify_sub *sub);
+gboolean _ip_stop_watching  (inotify_sub *sub);
+
+#endif
diff --git a/gio/inotify/inotify-sub.c b/gio/inotify/inotify-sub.c
new file mode 100644 (file)
index 0000000..f67a563
--- /dev/null
@@ -0,0 +1,68 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 8 -*- */
+
+/* inotify-sub.c - GMonitor based on inotify.
+
+   Copyright (C) 2006 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Authors: 
+                John McCutchan <john@johnmccutchan.com>
+*/
+
+#include "config.h"
+#include <string.h>
+#include <glib.h>
+
+#include "inotify-sub.h"
+
+static gboolean is_debug_enabled = FALSE;
+#define IS_W if (is_debug_enabled) g_warning
+
+static gchar*
+dup_dirname(const gchar* dirname)
+{
+  gchar* d_dirname = g_strdup (dirname);
+  size_t len = strlen (d_dirname);
+  
+  if (d_dirname[len] == '/')
+    d_dirname[len] = '\0';
+  
+  return d_dirname;
+}
+
+inotify_sub*
+_ih_sub_new (const gchar* dirname, const gchar* filename, gpointer user_data)
+{
+  inotify_sub* sub = NULL;
+  
+  sub = g_new0 (inotify_sub, 1);
+  sub->dirname = dup_dirname (dirname);
+  sub->filename = g_strdup (filename);
+  sub->user_data = user_data;
+  
+  IS_W ("new subscription for %s being setup\n", sub->dirname);
+  
+  return sub;
+}
+
+void
+_ih_sub_free (inotify_sub* sub)
+{
+  g_free (sub->dirname);
+  g_free (sub->filename);
+  g_free (sub);
+}
diff --git a/gio/inotify/inotify-sub.h b/gio/inotify/inotify-sub.h
new file mode 100644 (file)
index 0000000..36561e7
--- /dev/null
@@ -0,0 +1,38 @@
+/* inotify-sub.h - GVFS Directory Monitor using inotify
+
+   Copyright (C) 2006 John McCutchan
+
+   The Gnome Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: John McCutchan <john@johnmccutchan.com>
+*/
+
+
+#ifndef __INOTIFY_SUB_H
+#define __INOTIFY_SUB_H
+
+typedef struct
+{
+       gchar*   dirname;
+       gchar*   filename;
+       gboolean cancelled;
+       gpointer user_data;
+} inotify_sub;
+
+inotify_sub* _ih_sub_new (const gchar* dirname, const gchar* filename, gpointer user_data);
+void         _ih_sub_free (inotify_sub* sub);
+
+#endif /* __INOTIFY_SUB_H */
diff --git a/gio/inotify/local_inotify.h b/gio/inotify/local_inotify.h
new file mode 100644 (file)
index 0000000..267c88b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Inode based directory notification for Linux
+ *
+ * Copyright (C) 2005 John McCutchan
+ */
+
+#ifndef _LINUX_INOTIFY_H
+#define _LINUX_INOTIFY_H
+
+#include <linux/types.h>
+
+/*
+ * struct inotify_event - structure read from the inotify device for each event
+ *
+ * When you are watching a directory, you will receive the filename for events
+ * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.
+ */
+struct inotify_event {
+       __s32           wd;             /* watch descriptor */
+       __u32           mask;           /* watch mask */
+       __u32           cookie;         /* cookie to synchronize two events */
+       __u32           len;            /* length (including nulls) of name */
+       char            name[0];        /* stub for possible name */
+};
+
+/* the following are legal, implemented events that user-space can watch for */
+#define IN_ACCESS              0x00000001      /* File was accessed */
+#define IN_MODIFY              0x00000002      /* File was modified */
+#define IN_ATTRIB              0x00000004      /* Metadata changed */
+#define IN_CLOSE_WRITE         0x00000008      /* Writtable file was closed */
+#define IN_CLOSE_NOWRITE       0x00000010      /* Unwrittable file closed */
+#define IN_OPEN                        0x00000020      /* File was opened */
+#define IN_MOVED_FROM          0x00000040      /* File was moved from X */
+#define IN_MOVED_TO            0x00000080      /* File was moved to Y */
+#define IN_CREATE              0x00000100      /* Subfile was created */
+#define IN_DELETE              0x00000200      /* Subfile was deleted */
+#define IN_DELETE_SELF         0x00000400      /* Self was deleted */
+#define IN_MOVE_SELF           0x00000800      /* Self was moved */
+
+/* the following are legal events.  they are sent as needed to any watch */
+#define IN_UNMOUNT             0x00002000      /* Backing fs was unmounted */
+#define IN_Q_OVERFLOW          0x00004000      /* Event queued overflowed */
+#define IN_IGNORED             0x00008000      /* File was ignored */
+
+/* helper events */
+#define IN_CLOSE               (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */
+#define IN_MOVE                        (IN_MOVED_FROM | IN_MOVED_TO) /* moves */
+
+/* special flags */
+#define IN_ONLYDIR             0x01000000      /* only watch the path if it is a directory */
+#define IN_DONT_FOLLOW         0x02000000      /* don't follow a sym link */
+#define IN_MASK_ADD            0x20000000      /* add to the mask of an already existing watch */
+#define IN_ISDIR               0x40000000      /* event occurred against dir */
+#define IN_ONESHOT             0x80000000      /* only send event once */
+
+/*
+ * All of the events - we build the list by hand so that we can add flags in
+ * the future and not break backward compatibility.  Apps will get only the
+ * events that they originally wanted.  Be sure to add new events here!
+ */
+#define IN_ALL_EVENTS  (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \
+                        IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \
+                        IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \
+                        IN_MOVE_SELF)
+
+#ifdef __KERNEL__
+
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/config.h>
+
+#ifdef CONFIG_INOTIFY
+
+extern void inotify_inode_queue_event(struct inode *, __u32, __u32,
+                                     const char *);
+extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32,
+                                             const char *);
+extern void inotify_unmount_inodes(struct list_head *);
+extern void inotify_inode_is_dead(struct inode *);
+extern u32 inotify_get_cookie(void);
+
+#else
+
+static inline void inotify_inode_queue_event(struct inode *inode,
+                                            __u32 mask, __u32 cookie,
+                                            const char *filename)
+{
+}
+
+static inline void inotify_dentry_parent_queue_event(struct dentry *dentry,
+                                                    __u32 mask, __u32 cookie,
+                                                    const char *filename)
+{
+}
+
+static inline void inotify_unmount_inodes(struct list_head *list)
+{
+}
+
+static inline void inotify_inode_is_dead(struct inode *inode)
+{
+}
+
+static inline u32 inotify_get_cookie(void)
+{
+       return 0;
+}
+
+#endif /* CONFIG_INOTIFY */
+
+#endif /* __KERNEL __ */
+
+#endif /* _LINUX_INOTIFY_H */
diff --git a/gio/inotify/local_inotify_syscalls.h b/gio/inotify/local_inotify_syscalls.h
new file mode 100644 (file)
index 0000000..1821acc
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef _LINUX_INOTIFY_SYSCALLS_H
+#define _LINUX_INOTIFY_SYSCALLS_H
+
+#include <asm/types.h>
+#include <sys/syscall.h>
+#include <unistd.h>
+
+#if defined(__i386__)
+# define __NR_inotify_init     291
+# define __NR_inotify_add_watch        292
+# define __NR_inotify_rm_watch 293
+#elif defined(__x86_64__)
+# define __NR_inotify_init     253
+# define __NR_inotify_add_watch        254
+# define __NR_inotify_rm_watch 255
+#elif defined(__alpha__)
+# define __NR_inotify_init      444
+# define __NR_inotify_add_watch 445
+# define __NR_inotify_rm_watch  446
+#elif defined(__ppc__) || defined(__powerpc__) || defined(__powerpc64__)
+# define __NR_inotify_init      275
+# define __NR_inotify_add_watch 276
+# define __NR_inotify_rm_watch  277
+#elif defined(__sparc__) || defined (__sparc64__)
+# define __NR_inotify_init      151
+# define __NR_inotify_add_watch 152
+# define __NR_inotify_rm_watch  156
+#elif defined (__ia64__)
+# define __NR_inotify_init  1277
+# define __NR_inotify_add_watch 1278
+# define __NR_inotify_rm_watch  1279
+#elif defined (__s390__) || defined (__s390x__)
+# define __NR_inotify_init  284
+# define __NR_inotify_add_watch 285
+# define __NR_inotify_rm_watch  286
+#elif defined (__arm__)
+# define __NR_inotify_init  316
+# define __NR_inotify_add_watch 317
+# define __NR_inotify_rm_watch  318
+#elif defined (__SH4__)
+# define __NR_inotify_init  290
+# define __NR_inotify_add_watch 291
+# define __NR_inotify_rm_watch  292
+#elif defined (__SH5__)
+# define __NR_inotify_init  318
+# define __NR_inotify_add_watch 319
+# define __NR_inotify_rm_watch  320
+#else
+# warning "Unsupported architecture"
+#endif
+
+#if defined(__i386__) || defined(__x86_64) || defined(__alpha__) || defined(__ppc__) || defined(__sparc__) || defined(__powerpc__) || defined(__powerpc64__) || defined(__ia64__) || defined(__s390__)
+static inline int inotify_init (void)
+{
+       return syscall (__NR_inotify_init);
+}
+
+static inline int inotify_add_watch (int fd, const char *name, __u32 mask)
+{
+       return syscall (__NR_inotify_add_watch, fd, name, mask);
+}
+
+static inline int inotify_rm_watch (int fd, __u32 wd)
+{
+       return syscall (__NR_inotify_rm_watch, fd, wd);
+}
+#else
+static inline int inotify_init (void)
+{
+       return -1;
+}
+
+static inline int inotify_add_watch (int fd, const char *name, __u32 mask)
+{
+       return -1;
+}
+
+static inline int inotify_rm_watch (int fd, __u32 wd)
+{
+       return -1;
+}
+
+#endif
+
+#endif /* _LINUX_INOTIFY_SYSCALLS_H */
diff --git a/gio/xdgmime/.gitignore b/gio/xdgmime/.gitignore
new file mode 100644 (file)
index 0000000..56e6945
--- /dev/null
@@ -0,0 +1 @@
+test-mime
diff --git a/gio/xdgmime/Makefile.am b/gio/xdgmime/Makefile.am
new file mode 100644 (file)
index 0000000..41f16a7
--- /dev/null
@@ -0,0 +1,24 @@
+AM_CPPFLAGS = -DXDG_PREFIX=_gio_xdg
+
+noinst_LTLIBRARIES = libxdgmime.la
+
+libxdgmime_la_SOURCES = \
+       xdgmime.c       \
+       xdgmime.h       \
+       xdgmimealias.c  \
+       xdgmimealias.h  \
+       xdgmimecache.c  \
+       xdgmimecache.h  \
+       xdgmimeglob.c   \
+       xdgmimeglob.h   \
+       xdgmimeint.c    \
+       xdgmimeint.h    \
+       xdgmimemagic.c  \
+       xdgmimemagic.h  \
+       xdgmimeparent.c \
+       xdgmimeparent.h
+
+noinst_PROGRAMS = test-mime
+
+test_mime_LDADD = libxdgmime.la
+test_mime_SOURCES = test-mime.c
diff --git a/gio/xdgmime/test-mime.c b/gio/xdgmime/test-mime.c
new file mode 100644 (file)
index 0000000..7cff59b
--- /dev/null
@@ -0,0 +1,142 @@
+/* 
+ * Copyright (C) 2003,2004  Red Hat, Inc.
+ * Copyright (C) 2003,2004  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#include "xdgmime.h"
+#include "xdgmimeglob.h"
+#include <string.h>
+#include <stdio.h>
+
+
+static void
+test_individual_glob (const char  *glob,
+                     XdgGlobType  expected_type)
+{
+  XdgGlobType test_type;
+
+  test_type = _xdg_glob_determine_type (glob);
+  if (test_type != expected_type)
+    {
+      printf ("Test Failed: %s is of type %s, but %s is expected\n",
+             glob,
+             ((test_type == XDG_GLOB_LITERAL)?"XDG_GLOB_LITERAL":
+              ((test_type == XDG_GLOB_SIMPLE)?"XDG_GLOB_SIMPLE":"XDG_GLOB_FULL")),
+             ((expected_type == XDG_GLOB_LITERAL)?"XDG_GLOB_LITERAL":
+              ((expected_type == XDG_GLOB_SIMPLE)?"XDG_GLOB_SIMPLE":"XDG_GLOB_COMPLEX")));
+    }
+}
+
+static void
+test_glob_type (void)
+{
+  test_individual_glob ("*.gif", XDG_GLOB_SIMPLE);
+  test_individual_glob ("Foo*.gif", XDG_GLOB_FULL);
+  test_individual_glob ("*[4].gif", XDG_GLOB_FULL);
+  test_individual_glob ("Makefile", XDG_GLOB_LITERAL);
+  test_individual_glob ("sldkfjvlsdf\\\\slkdjf", XDG_GLOB_FULL);
+  test_individual_glob ("tree.[ch]", XDG_GLOB_FULL);
+}
+
+static void
+test_alias (const char *mime_a,
+           const char *mime_b,
+           int         expected)
+{
+  int actual;
+
+  actual = xdg_mime_mime_type_equal (mime_a, mime_b);
+
+  if (actual != expected)
+    {
+      printf ("Test Failed: %s is %s to %s\n", 
+             mime_a, actual ? "equal" : "not equal", mime_b);
+    }
+}
+
+static void
+test_aliasing (void)
+{
+  test_alias ("application/wordperfect", "application/vnd.wordperfect", 1);
+  test_alias ("application/x-gnome-app-info", "application/x-desktop", 1);
+  test_alias ("application/x-wordperfect", "application/vnd.wordperfect", 1);
+  test_alias ("application/x-wordperfect", "audio/x-midi", 0);
+  test_alias ("/", "vnd/vnd", 0);
+  test_alias ("application/octet-stream", "text/plain", 0);
+  test_alias ("text/plain", "text/*", 0);
+}
+
+static void
+test_subclass (const char *mime_a,
+              const char *mime_b,
+              int         expected)
+{
+  int actual;
+
+  actual = xdg_mime_mime_type_subclass (mime_a, mime_b);
+
+  if (actual != expected)
+    {
+      printf ("Test Failed: %s is %s of %s\n", 
+             mime_a, actual ? "subclass" : "not subclass", mime_b);
+    }
+}
+
+static void
+test_subclassing (void)
+{
+  test_subclass ("application/rtf", "text/plain", 1);
+  test_subclass ("message/news", "text/plain", 1);
+  test_subclass ("message/news", "message/*", 1);
+  test_subclass ("message/news", "text/*", 1);
+  test_subclass ("message/news", "application/octet-stream", 1);
+  test_subclass ("application/rtf", "application/octet-stream", 1);
+  test_subclass ("application/x-gnome-app-info", "text/plain", 1);
+  test_subclass ("image/x-djvu", "image/vnd.djvu", 1);
+  test_subclass ("image/vnd.djvu", "image/x-djvu", 1);
+  test_subclass ("image/vnd.djvu", "text/plain", 0);
+  test_subclass ("image/vnd.djvu", "text/*", 0);
+  test_subclass ("text/*", "text/plain", 0);
+}
+
+int
+main (int argc, char *argv[])
+{
+  const char *result;
+  const char *file_name;
+  int i;
+
+  test_glob_type ();
+  test_aliasing ();
+  test_subclassing ();
+
+  for (i = 1; i < argc; i++)
+    {
+      file_name = argv[i];
+      result = xdg_mime_get_mime_type_for_file (file_name, NULL);
+      printf ("File \"%s\" has a mime-type of %s\n", file_name, result);
+    }
+
+#if 0
+  xdg_mime_dump ();
+#endif
+  return 0;
+}
+     
diff --git a/gio/xdgmime/xdgmime.c b/gio/xdgmime/xdgmime.c
new file mode 100644 (file)
index 0000000..0e11b07
--- /dev/null
@@ -0,0 +1,864 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.c: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ * 
+ * Copyright (C) 2003,2004  Red Hat, Inc.
+ * Copyright (C) 2003,2004  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmime.h"
+#include "xdgmimeint.h"
+#include "xdgmimeglob.h"
+#include "xdgmimemagic.h"
+#include "xdgmimealias.h"
+#include "xdgmimeparent.h"
+#include "xdgmimecache.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <assert.h>
+
+typedef struct XdgDirTimeList XdgDirTimeList;
+typedef struct XdgCallbackList XdgCallbackList;
+
+static int need_reread = TRUE;
+static time_t last_stat_time = 0;
+
+static XdgGlobHash *global_hash = NULL;
+static XdgMimeMagic *global_magic = NULL;
+static XdgAliasList *alias_list = NULL;
+static XdgParentList *parent_list = NULL;
+static XdgDirTimeList *dir_time_list = NULL;
+static XdgCallbackList *callback_list = NULL;
+
+XdgMimeCache **_caches = NULL;
+static int n_caches = 0;
+
+const char xdg_mime_type_unknown[] = "application/octet-stream";
+
+
+enum
+{
+  XDG_CHECKED_UNCHECKED,
+  XDG_CHECKED_VALID,
+  XDG_CHECKED_INVALID
+};
+
+struct XdgDirTimeList
+{
+  time_t mtime;
+  char *directory_name;
+  int checked;
+  XdgDirTimeList *next;
+};
+
+struct XdgCallbackList
+{
+  XdgCallbackList *next;
+  XdgCallbackList *prev;
+  int              callback_id;
+  XdgMimeCallback  callback;
+  void            *data;
+  XdgMimeDestroy   destroy;
+};
+
+/* Function called by xdg_run_command_on_dirs.  If it returns TRUE, further
+ * directories aren't looked at */
+typedef int (*XdgDirectoryFunc) (const char *directory,
+                                void       *user_data);
+
+static XdgDirTimeList *
+xdg_dir_time_list_new (void)
+{
+  XdgDirTimeList *retval;
+
+  retval = calloc (1, sizeof (XdgDirTimeList));
+  retval->checked = XDG_CHECKED_UNCHECKED;
+
+  return retval;
+}
+
+static void
+xdg_dir_time_list_free (XdgDirTimeList *list)
+{
+  XdgDirTimeList *next;
+
+  while (list)
+    {
+      next = list->next;
+      free (list->directory_name);
+      free (list);
+      list = next;
+    }
+}
+
+static int
+xdg_mime_init_from_directory (const char *directory)
+{
+  char *file_name;
+  struct stat st;
+  XdgDirTimeList *list;
+
+  assert (directory != NULL);
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
+  if (stat (file_name, &st) == 0)
+    {
+      XdgMimeCache *cache = _xdg_mime_cache_new_from_file (file_name);
+
+      if (cache != NULL)
+       {
+         list = xdg_dir_time_list_new ();
+         list->directory_name = file_name;
+         list->mtime = st.st_mtime;
+         list->next = dir_time_list;
+         dir_time_list = list;
+
+         _caches = realloc (_caches, sizeof (XdgMimeCache *) * (n_caches + 2));
+         _caches[n_caches] = cache;
+          _caches[n_caches + 1] = NULL;
+         n_caches++;
+
+         return FALSE;
+       }
+    }
+  free (file_name);
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/globs");
+  if (stat (file_name, &st) == 0)
+    {
+      _xdg_mime_glob_read_from_file (global_hash, file_name);
+
+      list = xdg_dir_time_list_new ();
+      list->directory_name = file_name;
+      list->mtime = st.st_mtime;
+      list->next = dir_time_list;
+      dir_time_list = list;
+    }
+  else
+    {
+      free (file_name);
+    }
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/magic");
+  if (stat (file_name, &st) == 0)
+    {
+      _xdg_mime_magic_read_from_file (global_magic, file_name);
+
+      list = xdg_dir_time_list_new ();
+      list->directory_name = file_name;
+      list->mtime = st.st_mtime;
+      list->next = dir_time_list;
+      dir_time_list = list;
+    }
+  else
+    {
+      free (file_name);
+    }
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/aliases") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/aliases");
+  _xdg_mime_alias_read_from_file (alias_list, file_name);
+  free (file_name);
+
+  file_name = malloc (strlen (directory) + strlen ("/mime/subclasses") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/subclasses");
+  _xdg_mime_parent_read_from_file (parent_list, file_name);
+  free (file_name);
+
+  return FALSE; /* Keep processing */
+}
+
+/* Runs a command on all the directories in the search path */
+static void
+xdg_run_command_on_dirs (XdgDirectoryFunc  func,
+                        void             *user_data)
+{
+  const char *xdg_data_home;
+  const char *xdg_data_dirs;
+  const char *ptr;
+
+  xdg_data_home = getenv ("XDG_DATA_HOME");
+  if (xdg_data_home)
+    {
+      if ((func) (xdg_data_home, user_data))
+       return;
+    }
+  else
+    {
+      const char *home;
+
+      home = getenv ("HOME");
+      if (home != NULL)
+       {
+         char *guessed_xdg_home;
+         int stop_processing;
+
+         guessed_xdg_home = malloc (strlen (home) + strlen ("/.local/share/") + 1);
+         strcpy (guessed_xdg_home, home);
+         strcat (guessed_xdg_home, "/.local/share/");
+         stop_processing = (func) (guessed_xdg_home, user_data);
+         free (guessed_xdg_home);
+
+         if (stop_processing)
+           return;
+       }
+    }
+
+  xdg_data_dirs = getenv ("XDG_DATA_DIRS");
+  if (xdg_data_dirs == NULL)
+    xdg_data_dirs = "/usr/local/share/:/usr/share/";
+
+  ptr = xdg_data_dirs;
+
+  while (*ptr != '\000')
+    {
+      const char *end_ptr;
+      char *dir;
+      int len;
+      int stop_processing;
+
+      end_ptr = ptr;
+      while (*end_ptr != ':' && *end_ptr != '\000')
+       end_ptr ++;
+
+      if (end_ptr == ptr)
+       {
+         ptr++;
+         continue;
+       }
+
+      if (*end_ptr == ':')
+       len = end_ptr - ptr;
+      else
+       len = end_ptr - ptr + 1;
+      dir = malloc (len + 1);
+      strncpy (dir, ptr, len);
+      dir[len] = '\0';
+      stop_processing = (func) (dir, user_data);
+      free (dir);
+
+      if (stop_processing)
+       return;
+
+      ptr = end_ptr;
+    }
+}
+
+/* Checks file_path to make sure it has the same mtime as last time it was
+ * checked.  If it has a different mtime, or if the file doesn't exist, it
+ * returns FALSE.
+ *
+ * FIXME: This doesn't protect against permission changes.
+ */
+static int
+xdg_check_file (const char *file_path,
+                int        *exists)
+{
+  struct stat st;
+
+  /* If the file exists */
+  if (stat (file_path, &st) == 0)
+    {
+      XdgDirTimeList *list;
+
+      if (exists)
+        *exists = TRUE;
+
+      for (list = dir_time_list; list; list = list->next)
+       {
+         if (! strcmp (list->directory_name, file_path) &&
+             st.st_mtime == list->mtime)
+           {
+             if (list->checked == XDG_CHECKED_UNCHECKED)
+               list->checked = XDG_CHECKED_VALID;
+             else if (list->checked == XDG_CHECKED_VALID)
+               list->checked = XDG_CHECKED_INVALID;
+
+             return (list->checked != XDG_CHECKED_VALID);
+           }
+       }
+      return TRUE;
+    }
+
+  if (exists)
+    *exists = FALSE;
+
+  return FALSE;
+}
+
+static int
+xdg_check_dir (const char *directory,
+              int        *invalid_dir_list)
+{
+  int invalid, exists;
+  char *file_name;
+
+  assert (directory != NULL);
+
+  /* Check the mime.cache file */
+  file_name = malloc (strlen (directory) + strlen ("/mime/mime.cache") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/mime.cache");
+  invalid = xdg_check_file (file_name, &exists);
+  free (file_name);
+  if (invalid)
+    {
+      *invalid_dir_list = TRUE;
+      return TRUE;
+    }
+  else if (exists)
+    {
+      return FALSE;
+    }
+
+  /* Check the globs file */
+  file_name = malloc (strlen (directory) + strlen ("/mime/globs") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/globs");
+  invalid = xdg_check_file (file_name, NULL);
+  free (file_name);
+  if (invalid)
+    {
+      *invalid_dir_list = TRUE;
+      return TRUE;
+    }
+
+  /* Check the magic file */
+  file_name = malloc (strlen (directory) + strlen ("/mime/magic") + 1);
+  strcpy (file_name, directory); strcat (file_name, "/mime/magic");
+  invalid = xdg_check_file (file_name, NULL);
+  free (file_name);
+  if (invalid)
+    {
+      *invalid_dir_list = TRUE;
+      return TRUE;
+    }
+
+  return FALSE; /* Keep processing */
+}
+
+/* Walks through all the mime files stat()ing them to see if they've changed.
+ * Returns TRUE if they have. */
+static int
+xdg_check_dirs (void)
+{
+  XdgDirTimeList *list;
+  int invalid_dir_list = FALSE;
+
+  for (list = dir_time_list; list; list = list->next)
+    list->checked = XDG_CHECKED_UNCHECKED;
+
+  xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_check_dir,
+                          &invalid_dir_list);
+
+  if (invalid_dir_list)
+    return TRUE;
+
+  for (list = dir_time_list; list; list = list->next)
+    {
+      if (list->checked != XDG_CHECKED_VALID)
+       return TRUE;
+    }
+
+  return FALSE;
+}
+
+/* We want to avoid stat()ing on every single mime call, so we only look for
+ * newer files every 5 seconds.  This will return TRUE if we need to reread the
+ * mime data from disk.
+ */
+static int
+xdg_check_time_and_dirs (void)
+{
+  struct timeval tv;
+  time_t current_time;
+  int retval = FALSE;
+
+  gettimeofday (&tv, NULL);
+  current_time = tv.tv_sec;
+
+  if (current_time >= last_stat_time + 5)
+    {
+      retval = xdg_check_dirs ();
+      last_stat_time = current_time;
+    }
+
+  return retval;
+}
+
+/* Called in every public function.  It reloads the hash function if need be.
+ */
+static void
+xdg_mime_init (void)
+{
+  if (xdg_check_time_and_dirs ())
+    {
+      xdg_mime_shutdown ();
+    }
+
+  if (need_reread)
+    {
+      global_hash = _xdg_glob_hash_new ();
+      global_magic = _xdg_mime_magic_new ();
+      alias_list = _xdg_mime_alias_list_new ();
+      parent_list = _xdg_mime_parent_list_new ();
+
+      xdg_run_command_on_dirs ((XdgDirectoryFunc) xdg_mime_init_from_directory,
+                              NULL);
+
+      need_reread = FALSE;
+    }
+}
+
+const char *
+xdg_mime_get_mime_type_for_data (const void *data,
+                                size_t      len,
+                                int        *result_prio)
+{
+  const char *mime_type;
+
+  xdg_mime_init ();
+
+  if (_caches)
+    return _xdg_mime_cache_get_mime_type_for_data (data, len, result_prio);
+
+  mime_type = _xdg_mime_magic_lookup_data (global_magic, data, len, result_prio, NULL, 0);
+
+  if (mime_type)
+    return mime_type;
+
+  return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+xdg_mime_get_mime_type_for_file (const char  *file_name,
+                                 struct stat *statbuf)
+{
+  const char *mime_type;
+  /* currently, only a few globs occur twice, and none
+   * more often, so 5 seems plenty.
+   */
+  const char *mime_types[5];
+  FILE *file;
+  unsigned char *data;
+  int max_extent;
+  int bytes_read;
+  struct stat buf;
+  const char *base_name;
+  int n;
+
+  if (file_name == NULL)
+    return NULL;
+  if (! _xdg_utf8_validate (file_name))
+    return NULL;
+
+  xdg_mime_init ();
+
+  if (_caches)
+    return _xdg_mime_cache_get_mime_type_for_file (file_name, statbuf);
+
+  base_name = _xdg_get_base_name (file_name);
+  n = _xdg_glob_hash_lookup_file_name (global_hash, base_name, mime_types, 5);
+
+  if (n == 1)
+    return mime_types[0];
+
+  if (!statbuf)
+    {
+      if (stat (file_name, &buf) != 0)
+       return XDG_MIME_TYPE_UNKNOWN;
+
+      statbuf = &buf;
+    }
+
+  if (!S_ISREG (statbuf->st_mode))
+    return XDG_MIME_TYPE_UNKNOWN;
+
+  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
+   * be large and need getting from a stream instead of just reading it all
+   * in. */
+  max_extent = _xdg_mime_magic_get_buffer_extents (global_magic);
+  data = malloc (max_extent);
+  if (data == NULL)
+    return XDG_MIME_TYPE_UNKNOWN;
+        
+  file = fopen (file_name, "r");
+  if (file == NULL)
+    {
+      free (data);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  bytes_read = fread (data, 1, max_extent, file);
+  if (ferror (file))
+    {
+      free (data);
+      fclose (file);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  mime_type = _xdg_mime_magic_lookup_data (global_magic, data, bytes_read, NULL,
+                                          mime_types, n);
+
+  free (data);
+  fclose (file);
+
+  if (mime_type)
+    return mime_type;
+
+  return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+xdg_mime_get_mime_type_from_file_name (const char *file_name)
+{
+  const char *mime_type;
+
+  xdg_mime_init ();
+
+  if (_caches)
+    return _xdg_mime_cache_get_mime_type_from_file_name (file_name);
+
+  if (_xdg_glob_hash_lookup_file_name (global_hash, file_name, &mime_type, 1))
+    return mime_type;
+  else
+    return XDG_MIME_TYPE_UNKNOWN;
+}
+
+int
+xdg_mime_get_mime_types_from_file_name (const char *file_name,
+                                       const char  *mime_types[],
+                                       int          n_mime_types)
+{
+  xdg_mime_init ();
+  
+  if (_caches)
+    return _xdg_mime_cache_get_mime_types_from_file_name (file_name, mime_types, n_mime_types);
+  
+  return _xdg_glob_hash_lookup_file_name (global_hash, file_name, mime_types, n_mime_types);
+}
+
+int
+xdg_mime_is_valid_mime_type (const char *mime_type)
+{
+  /* FIXME: We should make this a better test
+   */
+  return _xdg_utf8_validate (mime_type);
+}
+
+void
+xdg_mime_shutdown (void)
+{
+  XdgCallbackList *list;
+
+  /* FIXME: Need to make this (and the whole library) thread safe */
+  if (dir_time_list)
+    {
+      xdg_dir_time_list_free (dir_time_list);
+      dir_time_list = NULL;
+    }
+       
+  if (global_hash)
+    {
+      _xdg_glob_hash_free (global_hash);
+      global_hash = NULL;
+    }
+  if (global_magic)
+    {
+      _xdg_mime_magic_free (global_magic);
+      global_magic = NULL;
+    }
+
+  if (alias_list)
+    {
+      _xdg_mime_alias_list_free (alias_list);
+      alias_list = NULL;
+    }
+
+  if (parent_list)
+    {
+      _xdg_mime_parent_list_free (parent_list);
+      parent_list = NULL;
+    }
+  
+  if (_caches)
+    {
+      int i;
+
+      for (i = 0; i < n_caches; i++)
+        _xdg_mime_cache_unref (_caches[i]);
+      free (_caches);
+      _caches = NULL;
+      n_caches = 0;
+    }
+
+  for (list = callback_list; list; list = list->next)
+    (list->callback) (list->data);
+
+  need_reread = TRUE;
+}
+
+int
+xdg_mime_get_max_buffer_extents (void)
+{
+  xdg_mime_init ();
+  
+  if (_caches)
+    return _xdg_mime_cache_get_max_buffer_extents ();
+
+  return _xdg_mime_magic_get_buffer_extents (global_magic);
+}
+
+const char *
+_xdg_mime_unalias_mime_type (const char *mime_type)
+{
+  const char *lookup;
+
+  if (_caches)
+    return _xdg_mime_cache_unalias_mime_type (mime_type);
+
+  if ((lookup = _xdg_mime_alias_list_lookup (alias_list, mime_type)) != NULL)
+    return lookup;
+
+  return mime_type;
+}
+
+const char *
+xdg_mime_unalias_mime_type (const char *mime_type)
+{
+  xdg_mime_init ();
+
+  return _xdg_mime_unalias_mime_type (mime_type);
+}
+
+int
+_xdg_mime_mime_type_equal (const char *mime_a,
+                          const char *mime_b)
+{
+  const char *unalias_a, *unalias_b;
+
+  unalias_a = _xdg_mime_unalias_mime_type (mime_a);
+  unalias_b = _xdg_mime_unalias_mime_type (mime_b);
+
+  if (strcmp (unalias_a, unalias_b) == 0)
+    return 1;
+
+  return 0;
+}
+
+int
+xdg_mime_mime_type_equal (const char *mime_a,
+                         const char *mime_b)
+{
+  xdg_mime_init ();
+
+  return _xdg_mime_mime_type_equal (mime_a, mime_b);
+}
+
+int
+xdg_mime_media_type_equal (const char *mime_a,
+                          const char *mime_b)
+{
+  char *sep;
+
+  xdg_mime_init ();
+
+  sep = strchr (mime_a, '/');
+  
+  if (sep && strncmp (mime_a, mime_b, sep - mime_a + 1) == 0)
+    return 1;
+
+  return 0;
+}
+
+#if 1
+static int
+xdg_mime_is_super_type (const char *mime)
+{
+  int length;
+  const char *type;
+
+  length = strlen (mime);
+  type = &(mime[length - 2]);
+
+  if (strcmp (type, "/*") == 0)
+    return 1;
+
+  return 0;
+}
+#endif
+
+int
+_xdg_mime_mime_type_subclass (const char *mime,
+                             const char *base)
+{
+  const char *umime, *ubase;
+  const char **parents;
+
+  if (_caches)
+    return _xdg_mime_cache_mime_type_subclass (mime, base);
+
+  umime = _xdg_mime_unalias_mime_type (mime);
+  ubase = _xdg_mime_unalias_mime_type (base);
+
+  if (strcmp (umime, ubase) == 0)
+    return 1;
+
+#if 1  
+  /* Handle supertypes */
+  if (xdg_mime_is_super_type (ubase) &&
+      xdg_mime_media_type_equal (umime, ubase))
+    return 1;
+#endif
+
+  /*  Handle special cases text/plain and application/octet-stream */
+  if (strcmp (ubase, "text/plain") == 0 && 
+      strncmp (umime, "text/", 5) == 0)
+    return 1;
+
+  if (strcmp (ubase, "application/octet-stream") == 0)
+    return 1;
+  
+  parents = _xdg_mime_parent_list_lookup (parent_list, umime);
+  for (; parents && *parents; parents++)
+    {
+      if (_xdg_mime_mime_type_subclass (*parents, ubase))
+       return 1;
+    }
+
+  return 0;
+}
+
+int
+xdg_mime_mime_type_subclass (const char *mime,
+                            const char *base)
+{
+  xdg_mime_init ();
+
+  return _xdg_mime_mime_type_subclass (mime, base);
+}
+
+char **
+xdg_mime_list_mime_parents (const char *mime)
+{
+  const char **parents;
+  char **result;
+  int i, n;
+
+  if (_caches)
+    return _xdg_mime_cache_list_mime_parents (mime);
+
+  parents = xdg_mime_get_mime_parents (mime);
+
+  if (!parents)
+    return NULL;
+
+  for (i = 0; parents[i]; i++) ;
+  
+  n = (i + 1) * sizeof (char *);
+  result = (char **) malloc (n);
+  memcpy (result, parents, n);
+
+  return result;
+}
+
+const char **
+xdg_mime_get_mime_parents (const char *mime)
+{
+  const char *umime;
+
+  xdg_mime_init ();
+
+  umime = _xdg_mime_unalias_mime_type (mime);
+
+  return _xdg_mime_parent_list_lookup (parent_list, umime);
+}
+
+void 
+xdg_mime_dump (void)
+{
+  printf ("*** ALIASES ***\n\n");
+  _xdg_mime_alias_list_dump (alias_list);
+  printf ("\n*** PARENTS ***\n\n");
+  _xdg_mime_parent_list_dump (parent_list);
+}
+
+
+/* Registers a function to be called every time the mime database reloads its files
+ */
+int
+xdg_mime_register_reload_callback (XdgMimeCallback  callback,
+                                  void            *data,
+                                  XdgMimeDestroy   destroy)
+{
+  XdgCallbackList *list_el;
+  static int callback_id = 1;
+
+  /* Make a new list element */
+  list_el = calloc (1, sizeof (XdgCallbackList));
+  list_el->callback_id = callback_id;
+  list_el->callback = callback;
+  list_el->data = data;
+  list_el->destroy = destroy;
+  list_el->next = callback_list;
+  if (list_el->next)
+    list_el->next->prev = list_el;
+
+  callback_list = list_el;
+  callback_id ++;
+
+  return callback_id - 1;
+}
+
+void
+xdg_mime_remove_callback (int callback_id)
+{
+  XdgCallbackList *list;
+
+  for (list = callback_list; list; list = list->next)
+    {
+      if (list->callback_id == callback_id)
+       {
+         if (list->next)
+           list->next = list->prev;
+
+         if (list->prev)
+           list->prev->next = list->next;
+         else
+           callback_list = list->next;
+
+         /* invoke the destroy handler */
+         (list->destroy) (list->data);
+         free (list);
+         return;
+       }
+    }
+}
diff --git a/gio/xdgmime/xdgmime.h b/gio/xdgmime/xdgmime.h
new file mode 100644 (file)
index 0000000..b8fd2d5
--- /dev/null
@@ -0,0 +1,120 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmime.h: XDG Mime Spec mime resolver.  Based on version 0.11 of the spec.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ * 
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+
+#ifndef __XDG_MIME_H__
+#define __XDG_MIME_H__
+
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#ifdef XDG_PREFIX
+#define XDG_ENTRY(func) _XDG_ENTRY2(XDG_PREFIX,func)
+#define _XDG_ENTRY2(prefix,func) _XDG_ENTRY3(prefix,func)
+#define _XDG_ENTRY3(prefix,func) prefix##_##func
+#endif
+
+typedef void (*XdgMimeCallback) (void *user_data);
+typedef void (*XdgMimeDestroy)  (void *user_data);
+
+  
+#ifdef XDG_PREFIX
+#define xdg_mime_get_mime_type_for_data       XDG_ENTRY(get_mime_type_for_data)
+#define xdg_mime_get_mime_type_for_file       XDG_ENTRY(get_mime_type_for_file)
+#define xdg_mime_get_mime_type_from_file_name XDG_ENTRY(get_mime_type_from_file_name)
+#define xdg_mime_get_mime_types_from_file_name XDG_ENTRY(get_mime_types_from_file_name)
+#define xdg_mime_is_valid_mime_type           XDG_ENTRY(is_valid_mime_type)
+#define xdg_mime_mime_type_equal              XDG_ENTRY(mime_type_equal)
+#define _xdg_mime_mime_type_equal             XDG_ENTRY(mime_type_equal_p)
+#define xdg_mime_media_type_equal             XDG_ENTRY(media_type_equal)
+#define xdg_mime_mime_type_subclass           XDG_ENTRY(mime_type_subclass)
+#define _xdg_mime_mime_type_subclass          XDG_ENTRY(mime_type_subclass_p)
+#define xdg_mime_get_mime_parents             XDG_ENTRY(get_mime_parents)
+#define xdg_mime_list_mime_parents            XDG_ENTRY(list_mime_parents)
+#define xdg_mime_unalias_mime_type            XDG_ENTRY(unalias_mime_type)
+#define _xdg_mime_unalias_mime_type           XDG_ENTRY(unalias_mime_type_p)  
+#define xdg_mime_get_max_buffer_extents       XDG_ENTRY(get_max_buffer_extents)
+#define xdg_mime_shutdown                     XDG_ENTRY(shutdown)
+#define xdg_mime_dump                         XDG_ENTRY(dump)
+#define xdg_mime_register_reload_callback     XDG_ENTRY(register_reload_callback)
+#define xdg_mime_remove_callback              XDG_ENTRY(remove_callback)
+#define xdg_mime_type_unknown                 XDG_ENTRY(type_unknown)
+#endif
+
+extern const char xdg_mime_type_unknown[];
+#define XDG_MIME_TYPE_UNKNOWN xdg_mime_type_unknown
+
+const char  *xdg_mime_get_mime_type_for_data       (const void *data,
+                                                   size_t      len,
+                                                   int        *result_prio);
+const char  *xdg_mime_get_mime_type_for_file       (const char *file_name,
+                                                    struct stat *statbuf);
+const char  *xdg_mime_get_mime_type_from_file_name (const char *file_name);
+int          xdg_mime_get_mime_types_from_file_name(const char *file_name,
+                                                   const char *mime_types[],
+                                                   int         n_mime_types);
+int          xdg_mime_is_valid_mime_type           (const char *mime_type);
+int          xdg_mime_mime_type_equal              (const char *mime_a,
+                                                   const char *mime_b);
+int          xdg_mime_media_type_equal             (const char *mime_a,
+                                                   const char *mime_b);
+int          xdg_mime_mime_type_subclass           (const char *mime_a,
+                                                   const char *mime_b);
+  /* xdg_mime_get_mime_parents() is deprecated since it does
+   * not work correctly with caches. Use xdg_mime_list_parents() 
+   * instead, but notice that that function expects you to free
+   * the array it returns. 
+   */
+const char **xdg_mime_get_mime_parents            (const char *mime);
+char **      xdg_mime_list_mime_parents                   (const char *mime);
+const char  *xdg_mime_unalias_mime_type                   (const char *mime);
+int          xdg_mime_get_max_buffer_extents       (void);
+void         xdg_mime_shutdown                     (void);
+void         xdg_mime_dump                         (void);
+int          xdg_mime_register_reload_callback     (XdgMimeCallback  callback,
+                                                   void            *data,
+                                                   XdgMimeDestroy   destroy);
+void         xdg_mime_remove_callback              (int              callback_id);
+
+   /* Private versions of functions that don't call xdg_mime_init () */
+int          _xdg_mime_mime_type_equal             (const char *mime_a,
+                                                   const char *mime_b);
+int          _xdg_mime_media_type_equal            (const char *mime_a,
+                                                   const char *mime_b);
+int          _xdg_mime_mime_type_subclass          (const char *mime,
+                                                   const char *base);
+const char  *_xdg_mime_unalias_mime_type           (const char *mime);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+#endif /* __XDG_MIME_H__ */
diff --git a/gio/xdgmime/xdgmimealias.c b/gio/xdgmime/xdgmimealias.c
new file mode 100644 (file)
index 0000000..07d89eb
--- /dev/null
@@ -0,0 +1,184 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file.  Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2004  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimealias.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef struct XdgAlias XdgAlias;
+
+struct XdgAlias 
+{
+  char *alias;
+  char *mime_type;
+};
+
+struct XdgAliasList
+{
+  struct XdgAlias *aliases;
+  int n_aliases;
+};
+
+XdgAliasList *
+_xdg_mime_alias_list_new (void)
+{
+  XdgAliasList *list;
+
+  list = malloc (sizeof (XdgAliasList));
+
+  list->aliases = NULL;
+  list->n_aliases = 0;
+
+  return list;
+}
+
+void         
+_xdg_mime_alias_list_free (XdgAliasList *list)
+{
+  int i;
+
+  if (list->aliases)
+    {
+      for (i = 0; i < list->n_aliases; i++)
+       {
+         free (list->aliases[i].alias);
+         free (list->aliases[i].mime_type);
+       }
+      free (list->aliases);
+    }
+  free (list);
+}
+
+static int
+alias_entry_cmp (const void *v1, const void *v2)
+{
+  return strcmp (((XdgAlias *)v1)->alias, ((XdgAlias *)v2)->alias);
+}
+
+const char  *
+_xdg_mime_alias_list_lookup (XdgAliasList *list,
+                            const char   *alias)
+{
+  XdgAlias *entry;
+  XdgAlias key;
+
+  if (list->n_aliases > 0)
+    {
+      key.alias = (char *)alias;
+      key.mime_type = NULL;
+
+      entry = bsearch (&key, list->aliases, list->n_aliases,
+                      sizeof (XdgAlias), alias_entry_cmp);
+      if (entry)
+        return entry->mime_type;
+    }
+
+  return NULL;
+}
+
+void
+_xdg_mime_alias_read_from_file (XdgAliasList *list,
+                               const char   *file_name)
+{
+  FILE *file;
+  char line[255];
+  int alloc;
+
+  file = fopen (file_name, "r");
+
+  if (file == NULL)
+    return;
+
+  /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
+   * Blah */
+  alloc = list->n_aliases + 16;
+  list->aliases = realloc (list->aliases, alloc * sizeof (XdgAlias));
+  while (fgets (line, 255, file) != NULL)
+    {
+      char *sep;
+      if (line[0] == '#')
+       continue;
+
+      sep = strchr (line, ' ');
+      if (sep == NULL)
+       continue;
+      *(sep++) = '\000';
+      sep[strlen (sep) -1] = '\000';
+      if (list->n_aliases == alloc)
+       {
+         alloc <<= 1;
+         list->aliases = realloc (list->aliases, 
+                                  alloc * sizeof (XdgAlias));
+       }
+      list->aliases[list->n_aliases].alias = strdup (line);
+      list->aliases[list->n_aliases].mime_type = strdup (sep);
+      list->n_aliases++;
+    }
+  list->aliases = realloc (list->aliases, 
+                          list->n_aliases * sizeof (XdgAlias));
+
+  fclose (file);  
+  
+  if (list->n_aliases > 1)
+    qsort (list->aliases, list->n_aliases, 
+           sizeof (XdgAlias), alias_entry_cmp);
+}
+
+
+void
+_xdg_mime_alias_list_dump (XdgAliasList *list)
+{
+  int i;
+
+  if (list->aliases)
+    {
+      for (i = 0; i < list->n_aliases; i++)
+       {
+         printf ("%s %s\n", 
+                 list->aliases[i].alias,
+                 list->aliases[i].mime_type);
+       }
+    }
+}
+
+
diff --git a/gio/xdgmime/xdgmimealias.h b/gio/xdgmime/xdgmimealias.h
new file mode 100644 (file)
index 0000000..d0aaed0
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.h: Private file.  Datastructure for storing the aliases.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 200  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_ALIAS_H__
+#define __XDG_MIME_ALIAS_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgAliasList XdgAliasList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_alias_read_from_file        XDG_ENTRY(alias_read_from_file)
+#define _xdg_mime_alias_list_new              XDG_ENTRY(alias_list_new)
+#define _xdg_mime_alias_list_free             XDG_ENTRY(alias_list_free)
+#define _xdg_mime_alias_list_lookup           XDG_ENTRY(alias_list_lookup)
+#define _xdg_mime_alias_list_dump             XDG_ENTRY(alias_list_dump)
+#endif
+
+void          _xdg_mime_alias_read_from_file (XdgAliasList *list,
+                                             const char   *file_name);
+XdgAliasList *_xdg_mime_alias_list_new       (void);
+void          _xdg_mime_alias_list_free      (XdgAliasList *list);
+const char   *_xdg_mime_alias_list_lookup    (XdgAliasList *list,
+                                             const char  *alias);
+void          _xdg_mime_alias_list_dump      (XdgAliasList *list);
+
+#endif /* __XDG_MIME_ALIAS_H__ */
diff --git a/gio/xdgmime/xdgmimecache.c b/gio/xdgmime/xdgmimecache.c
new file mode 100644 (file)
index 0000000..f17ddf2
--- /dev/null
@@ -0,0 +1,909 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file.  mmappable caches for mime data
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2005  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <fnmatch.h>
+#include <assert.h>
+
+#include <netinet/in.h> /* for ntohl/ntohs */
+
+#ifdef HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "xdgmimecache.h"
+#include "xdgmimeint.h"
+
+#ifndef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+#ifndef _O_BINARY
+#define _O_BINARY 0
+#endif
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+#define MAJOR_VERSION 1
+#define MINOR_VERSION 0
+
+struct _XdgMimeCache
+{
+  int ref_count;
+
+  size_t  size;
+  char   *buffer;
+};
+
+#define GET_UINT16(cache,offset) (ntohs(*(xdg_uint16_t*)((cache) + (offset))))
+#define GET_UINT32(cache,offset) (ntohl(*(xdg_uint32_t*)((cache) + (offset))))
+
+XdgMimeCache *
+_xdg_mime_cache_ref (XdgMimeCache *cache)
+{
+  cache->ref_count++;
+  return cache;
+}
+
+void
+_xdg_mime_cache_unref (XdgMimeCache *cache)
+{
+  cache->ref_count--;
+
+  if (cache->ref_count == 0)
+    {
+#ifdef HAVE_MMAP
+      munmap (cache->buffer, cache->size);
+#endif
+      free (cache);
+    }
+}
+
+XdgMimeCache *
+_xdg_mime_cache_new_from_file (const char *file_name)
+{
+  XdgMimeCache *cache = NULL;
+
+#ifdef HAVE_MMAP
+  int fd = -1;
+  struct stat st;
+  char *buffer = NULL;
+
+  /* Open the file and map it into memory */
+  fd = open (file_name, O_RDONLY|_O_BINARY, 0);
+
+  if (fd < 0)
+    return NULL;
+  
+  if (fstat (fd, &st) < 0 || st.st_size < 4)
+    goto done;
+
+  buffer = (char *) mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);
+
+  if (buffer == MAP_FAILED)
+    goto done;
+
+  /* Verify version */
+  if (GET_UINT16 (buffer, 0) != MAJOR_VERSION ||
+      GET_UINT16 (buffer, 2) != MINOR_VERSION)
+    {
+      munmap (buffer, st.st_size);
+
+      goto done;
+    }
+  
+  cache = (XdgMimeCache *) malloc (sizeof (XdgMimeCache));
+  cache->ref_count = 1;
+  cache->buffer = buffer;
+  cache->size = st.st_size;
+
+ done:
+  if (fd != -1)
+    close (fd);
+
+#endif  /* HAVE_MMAP */
+
+  return cache;
+}
+
+static int
+cache_magic_matchlet_compare_to_data (XdgMimeCache *cache, 
+                                     xdg_uint32_t  offset,
+                                     const void   *data,
+                                     size_t        len)
+{
+  xdg_uint32_t range_start = GET_UINT32 (cache->buffer, offset);
+  xdg_uint32_t range_length = GET_UINT32 (cache->buffer, offset + 4);
+  xdg_uint32_t data_length = GET_UINT32 (cache->buffer, offset + 12);
+  xdg_uint32_t data_offset = GET_UINT32 (cache->buffer, offset + 16);
+  xdg_uint32_t mask_offset = GET_UINT32 (cache->buffer, offset + 20);
+  
+  int i, j;
+
+  for (i = range_start; i <= range_start + range_length; i++)
+    {
+      int valid_matchlet = TRUE;
+      
+      if (i + data_length > len)
+       return FALSE;
+
+      if (mask_offset)
+       {
+         for (j = 0; j < data_length; j++)
+           {
+             if ((((unsigned char *)cache->buffer)[data_offset + j] & ((unsigned char *)cache->buffer)[mask_offset + j]) !=
+                 ((((unsigned char *) data)[j + i]) & ((unsigned char *)cache->buffer)[mask_offset + j]))
+               {
+                 valid_matchlet = FALSE;
+                 break;
+               }
+           }
+       }
+      else
+       {
+         for (j = 0; j < data_length; j++)
+           {
+             if (((unsigned char *)cache->buffer)[data_offset + j] != ((unsigned char *) data)[j + i])
+               {
+                 valid_matchlet = FALSE;
+                 break;
+               }
+           }
+       }
+      
+      if (valid_matchlet)
+       return TRUE;
+    }
+  
+  return FALSE;  
+}
+
+static int
+cache_magic_matchlet_compare (XdgMimeCache *cache, 
+                             xdg_uint32_t  offset,
+                             const void   *data,
+                             size_t        len)
+{
+  xdg_uint32_t n_children = GET_UINT32 (cache->buffer, offset + 24);
+  xdg_uint32_t child_offset = GET_UINT32 (cache->buffer, offset + 28);
+
+  int i;
+  
+  if (cache_magic_matchlet_compare_to_data (cache, offset, data, len))
+    {
+      if (n_children == 0)
+       return TRUE;
+      
+      for (i = 0; i < n_children; i++)
+       {
+         if (cache_magic_matchlet_compare (cache, child_offset + 32 * i,
+                                           data, len))
+           return TRUE;
+       }
+    }
+  
+  return FALSE;  
+}
+
+static const char *
+cache_magic_compare_to_data (XdgMimeCache *cache, 
+                            xdg_uint32_t  offset,
+                            const void   *data, 
+                            size_t        len, 
+                            int          *prio)
+{
+  xdg_uint32_t priority = GET_UINT32 (cache->buffer, offset);
+  xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, offset + 4);
+  xdg_uint32_t n_matchlets = GET_UINT32 (cache->buffer, offset + 8);
+  xdg_uint32_t matchlet_offset = GET_UINT32 (cache->buffer, offset + 12);
+
+  int i;
+
+  for (i = 0; i < n_matchlets; i++)
+    {
+      if (cache_magic_matchlet_compare (cache, matchlet_offset + i * 32, 
+                                       data, len))
+       {
+         *prio = priority;
+         
+         return cache->buffer + mimetype_offset;
+       }
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_magic_lookup_data (XdgMimeCache *cache, 
+                        const void   *data, 
+                        size_t        len, 
+                        int          *prio,
+                        const char   *mime_types[],
+                        int           n_mime_types)
+{
+  xdg_uint32_t list_offset;
+  xdg_uint32_t n_entries;
+  xdg_uint32_t offset;
+
+  int j, n;
+
+  *prio = 0;
+
+  list_offset = GET_UINT32 (cache->buffer, 24);
+  n_entries = GET_UINT32 (cache->buffer, list_offset);
+  offset = GET_UINT32 (cache->buffer, list_offset + 8);
+  
+  for (j = 0; j < n_entries; j++)
+    {
+      const char *match;
+
+      match = cache_magic_compare_to_data (cache, offset + 16 * j, 
+                                          data, len, prio);
+      if (match)
+       return match;
+      else
+       {
+         xdg_uint32_t mimetype_offset;
+         const char *non_match;
+         
+         mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * j + 4);
+         non_match = cache->buffer + mimetype_offset;
+
+         for (n = 0; n < n_mime_types; n++)
+           {
+             if (mime_types[n] && 
+                 xdg_mime_mime_type_equal (mime_types[n], non_match))
+               mime_types[n] = NULL;
+           }
+       }
+    }
+
+  return NULL;
+}
+
+static const char *
+cache_alias_lookup (const char *alias)
+{
+  const char *ptr;
+  int i, min, max, mid, cmp;
+
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 4);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset;
+
+      min = 0; 
+      max = n_entries - 1;
+      while (max >= min) 
+       {
+         mid = (min + max) / 2;
+
+         offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
+         ptr = cache->buffer + offset;
+         cmp = strcmp (ptr, alias);
+         
+         if (cmp < 0)
+           min = mid + 1;
+         else if (cmp > 0)
+           max = mid - 1;
+         else
+           {
+             offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
+             return cache->buffer + offset;
+           }
+       }
+    }
+
+  return NULL;
+}
+
+static int
+cache_glob_lookup_literal (const char *file_name,
+                          const char *mime_types[],
+                          int         n_mime_types)
+{
+  const char *ptr;
+  int i, min, max, mid, cmp;
+
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 12);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset;
+
+      min = 0; 
+      max = n_entries - 1;
+      while (max >= min) 
+       {
+         mid = (min + max) / 2;
+
+         offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid);
+         ptr = cache->buffer + offset;
+         cmp = strcmp (ptr, file_name);
+         
+         if (cmp < 0)
+           min = mid + 1;
+         else if (cmp > 0)
+           max = mid - 1;
+         else
+           {
+             offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * mid + 4);
+             mime_types[0] = (const char *)(cache->buffer + offset);
+             
+             return 1;
+           }
+       }
+    }
+
+  return 0;
+}
+
+static int
+cache_glob_lookup_fnmatch (const char *file_name,
+                          const char *mime_types[],
+                          int         n_mime_types)
+{
+  const char *mime_type;
+  const char *ptr;
+
+  int i, j, n;
+
+  n = 0;
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 20);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+
+      for (j = 0; j < n_entries && n < n_mime_types; j++)
+       {
+         xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
+         xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
+         ptr = cache->buffer + offset;
+         mime_type = cache->buffer + mimetype_offset;
+
+         /* FIXME: Not UTF-8 safe */
+         if (fnmatch (ptr, file_name, 0) == 0)
+           mime_types[n++] = mime_type;
+       }
+
+      if (n > 0)
+       return n;
+    }
+  
+  return 0;
+}
+
+static int
+cache_glob_node_lookup_suffix (XdgMimeCache *cache,
+                              xdg_uint32_t  n_entries,
+                              xdg_uint32_t  offset,
+                              const char   *suffix, 
+                              int           ignore_case,
+                              const char   *mime_types[],
+                              int           n_mime_types)
+{
+  xdg_unichar_t character;
+  xdg_unichar_t match_char;
+  xdg_uint32_t mimetype_offset;
+  xdg_uint32_t n_children;
+  xdg_uint32_t child_offset; 
+
+  int min, max, mid, n, i;
+
+  character = _xdg_utf8_to_ucs4 (suffix);
+  if (ignore_case)
+    character = _xdg_ucs4_to_lower (character);
+
+  min = 0;
+  max = n_entries - 1;
+  while (max >= min)
+    {
+      mid = (min + max) /  2;
+
+      match_char = GET_UINT32 (cache->buffer, offset + 16 * mid);
+
+      if (match_char < character)
+       min = mid + 1;
+      else if (match_char > character)
+       max = mid - 1;
+      else 
+       {
+         suffix = _xdg_utf8_next_char (suffix);
+         if (*suffix == '\0')
+           {
+             mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 4);
+             n = 0;
+             if (mimetype_offset)
+               mime_types[n++] = cache->buffer + mimetype_offset;
+             
+             n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
+             child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
+             i = 0;
+             while (n < n_mime_types && i < n_children)
+               {
+                 match_char = GET_UINT32 (cache->buffer, child_offset + 16 * i);
+                 mimetype_offset = GET_UINT32 (cache->buffer, offset + 16 * i + 4);
+                 if (match_char != 0)
+                   break;
+
+                 mime_types[n++] = cache->buffer + mimetype_offset;
+                 i++;
+               }
+
+             return n;
+           }
+         else
+           {
+             n_children = GET_UINT32 (cache->buffer, offset + 16 * mid + 8);
+             child_offset = GET_UINT32 (cache->buffer, offset + 16 * mid + 12);
+      
+             return cache_glob_node_lookup_suffix (cache, 
+                                                   n_children, child_offset,
+                                                   suffix, ignore_case,
+                                                   mime_types,
+                                                   n_mime_types);
+           }
+       }
+    }
+
+  return 0;
+}
+
+static int
+cache_glob_lookup_suffix (const char *suffix, 
+                         int         ignore_case,
+                         const char *mime_types[],
+                         int         n_mime_types)
+{
+  int i, n;
+
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
+
+      n = cache_glob_node_lookup_suffix (cache, 
+                                        n_entries, offset, 
+                                        suffix, ignore_case,
+                                        mime_types,
+                                        n_mime_types);
+      if (n > 0)
+       return n;
+    }
+
+  return 0;
+}
+
+static void
+find_stopchars (char *stopchars)
+{
+  int i, j, k, l;
+  k = 0;
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 16);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset = GET_UINT32 (cache->buffer, list_offset + 4);
+
+      for (j = 0; j < n_entries; j++)
+       {
+         xdg_uint32_t match_char = GET_UINT32 (cache->buffer, offset);
+         
+         if (match_char < 128)
+           {
+             for (l = 0; l < k; l++)
+               if (stopchars[l] == match_char)
+                 break;
+             if (l == k)
+               {
+                 stopchars[k] = (char) match_char;
+                 k++;
+               }
+           }
+
+         offset += 16;
+       }
+    }
+
+  stopchars[k] = '\0';
+}
+
+static int
+cache_glob_lookup_file_name (const char *file_name, 
+                            const char *mime_types[],
+                            int         n_mime_types)
+{
+  const char *ptr;
+  char stopchars[128];
+  int n;
+  
+  assert (file_name != NULL);
+
+  /* First, check the literals */
+  n = cache_glob_lookup_literal (file_name, mime_types, n_mime_types);
+  if (n > 0)
+    return n;
+
+  find_stopchars (stopchars);
+
+  /* Next, check suffixes */
+  ptr = strpbrk (file_name, stopchars);
+  while (ptr)
+    {
+      n = cache_glob_lookup_suffix (ptr, FALSE, mime_types, n_mime_types);
+      if (n > 0)
+       return n;
+      
+      n = cache_glob_lookup_suffix (ptr, TRUE, mime_types, n_mime_types);
+      if (n > 0)
+       return n;
+
+      ptr = strpbrk (ptr + 1, stopchars);
+    }
+  
+  /* Last, try fnmatch */
+  return cache_glob_lookup_fnmatch (file_name, mime_types, n_mime_types);
+}
+
+int
+_xdg_mime_cache_get_max_buffer_extents (void)
+{
+  xdg_uint32_t offset;
+  xdg_uint32_t max_extent;
+  int i;
+
+  max_extent = 0;
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+
+      offset = GET_UINT32 (cache->buffer, 24);
+      max_extent = MAX (max_extent, GET_UINT32 (cache->buffer, offset + 4));
+    }
+
+  return max_extent;
+}
+
+static const char *
+cache_get_mime_type_for_data (const void *data,
+                             size_t      len,
+                             int        *result_prio,
+                             const char *mime_types[],
+                             int         n_mime_types)
+{
+  const char *mime_type;
+  int i, n, priority;
+
+  priority = 0;
+  mime_type = NULL;
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+
+      int prio;
+      const char *match;
+
+      match = cache_magic_lookup_data (cache, data, len, &prio, 
+                                      mime_types, n_mime_types);
+      if (prio > priority)
+       {
+         priority = prio;
+         mime_type = match;
+       }
+    }
+
+  if (result_prio)
+    *result_prio = priority;
+  
+  if (priority > 0)
+    return mime_type;
+
+  for (n = 0; n < n_mime_types; n++)
+    {
+      
+      if (mime_types[n])
+       return mime_types[n];
+    }
+
+  return XDG_MIME_TYPE_UNKNOWN;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_for_data (const void *data,
+                                       size_t      len,
+                                       int        *result_prio)
+{
+  return cache_get_mime_type_for_data (data, len, result_prio, NULL, 0);
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_for_file (const char  *file_name,
+                                       struct stat *statbuf)
+{
+  const char *mime_type;
+  const char *mime_types[2];
+  FILE *file;
+  unsigned char *data;
+  int max_extent;
+  int bytes_read;
+  struct stat buf;
+  const char *base_name;
+  int n;
+
+  if (file_name == NULL)
+    return NULL;
+
+  if (! _xdg_utf8_validate (file_name))
+    return NULL;
+
+  base_name = _xdg_get_base_name (file_name);
+  n = cache_glob_lookup_file_name (base_name, mime_types, 2);
+
+  if (n == 1)
+    return mime_types[0];
+
+  if (!statbuf)
+    {
+      if (stat (file_name, &buf) != 0)
+       return XDG_MIME_TYPE_UNKNOWN;
+
+      statbuf = &buf;
+    }
+
+  if (!S_ISREG (statbuf->st_mode))
+    return XDG_MIME_TYPE_UNKNOWN;
+
+  /* FIXME: Need to make sure that max_extent isn't totally broken.  This could
+   * be large and need getting from a stream instead of just reading it all
+   * in. */
+  max_extent = _xdg_mime_cache_get_max_buffer_extents ();
+  data = malloc (max_extent);
+  if (data == NULL)
+    return XDG_MIME_TYPE_UNKNOWN;
+        
+  file = fopen (file_name, "r");
+  if (file == NULL)
+    {
+      free (data);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  bytes_read = fread (data, 1, max_extent, file);
+  if (ferror (file))
+    {
+      free (data);
+      fclose (file);
+      return XDG_MIME_TYPE_UNKNOWN;
+    }
+
+  mime_type = cache_get_mime_type_for_data (data, bytes_read, NULL,
+                                           mime_types, n);
+
+  free (data);
+  fclose (file);
+
+  return mime_type;
+}
+
+const char *
+_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name)
+{
+  const char *mime_type;
+
+  if (cache_glob_lookup_file_name (file_name, &mime_type, 1))
+    return mime_type;
+  else
+    return XDG_MIME_TYPE_UNKNOWN;
+}
+
+int
+_xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
+                                              const char  *mime_types[],
+                                              int          n_mime_types)
+{
+  return cache_glob_lookup_file_name (file_name, mime_types, n_mime_types);
+}
+
+#if 1
+static int
+is_super_type (const char *mime)
+{
+  int length;
+  const char *type;
+
+  length = strlen (mime);
+  type = &(mime[length - 2]);
+
+  if (strcmp (type, "/*") == 0)
+    return 1;
+
+  return 0;
+}
+#endif
+
+int
+_xdg_mime_cache_mime_type_subclass (const char *mime,
+                                   const char *base)
+{
+  const char *umime, *ubase;
+
+  int i, j, min, max, med, cmp;
+  
+  umime = _xdg_mime_cache_unalias_mime_type (mime);
+  ubase = _xdg_mime_cache_unalias_mime_type (base);
+
+  if (strcmp (umime, ubase) == 0)
+    return 1;
+
+  /* We really want to handle text/ * in GtkFileFilter, so we just
+   * turn on the supertype matching
+   */
+#if 1
+  /* Handle supertypes */
+  if (is_super_type (ubase) &&
+      xdg_mime_media_type_equal (umime, ubase))
+    return 1;
+#endif
+
+  /*  Handle special cases text/plain and application/octet-stream */
+  if (strcmp (ubase, "text/plain") == 0 && 
+      strncmp (umime, "text/", 5) == 0)
+    return 1;
+
+  if (strcmp (ubase, "application/octet-stream") == 0)
+    return 1;
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+      
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+      xdg_uint32_t offset, n_parents, parent_offset;
+
+      min = 0; 
+      max = n_entries - 1;
+      while (max >= min)
+       {
+         med = (min + max)/2;
+         
+         offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med);
+         cmp = strcmp (cache->buffer + offset, umime);
+         if (cmp < 0)
+           min = med + 1;
+         else if (cmp > 0)
+           max = med - 1;
+         else
+           {
+             offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * med + 4);
+             n_parents = GET_UINT32 (cache->buffer, offset);
+             
+             for (j = 0; j < n_parents; j++)
+               {
+                 parent_offset = GET_UINT32 (cache->buffer, offset + 4 + 4 * j);
+                 if (_xdg_mime_cache_mime_type_subclass (cache->buffer + parent_offset, ubase))
+                   return 1;
+               }
+
+             break;
+           }
+       }
+    }
+
+  return 0;
+}
+
+const char *
+_xdg_mime_cache_unalias_mime_type (const char *mime)
+{
+  const char *lookup;
+  
+  lookup = cache_alias_lookup (mime);
+  
+  if (lookup)
+    return lookup;
+  
+  return mime;  
+}
+
+char **
+_xdg_mime_cache_list_mime_parents (const char *mime)
+{
+  int i, j, k, p;
+  char *all_parents[128]; /* we'll stop at 128 */ 
+  char **result;
+
+  mime = xdg_mime_unalias_mime_type (mime);
+
+  p = 0;
+  for (i = 0; _caches[i]; i++)
+    {
+      XdgMimeCache *cache = _caches[i];
+  
+      xdg_uint32_t list_offset = GET_UINT32 (cache->buffer, 8);
+      xdg_uint32_t n_entries = GET_UINT32 (cache->buffer, list_offset);
+
+      for (j = 0; j < n_entries; j++)
+       {
+         xdg_uint32_t mimetype_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j);
+         xdg_uint32_t parents_offset = GET_UINT32 (cache->buffer, list_offset + 4 + 8 * j + 4);
+
+         if (strcmp (cache->buffer + mimetype_offset, mime) == 0)
+           {
+             xdg_uint32_t parent_mime_offset;
+             xdg_uint32_t n_parents = GET_UINT32 (cache->buffer, parents_offset);
+
+             for (k = 0; k < n_parents && p < 127; k++)
+               {
+                 parent_mime_offset = GET_UINT32 (cache->buffer, parents_offset + 4 + 4 * k);
+                 all_parents[p++] = cache->buffer + parent_mime_offset;
+               }
+
+             break;
+           }
+       }
+    }
+  all_parents[p++] = 0;
+  
+  result = (char **) malloc (p * sizeof (char *));
+  memcpy (result, all_parents, p * sizeof (char *));
+
+  return result;
+}
+
diff --git a/gio/xdgmime/xdgmimecache.h b/gio/xdgmime/xdgmimecache.h
new file mode 100644 (file)
index 0000000..1cd978f
--- /dev/null
@@ -0,0 +1,76 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimecache.h: Private file.  Datastructure for mmapped caches.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2005  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_CACHE_H__
+#define __XDG_MIME_CACHE_H__
+
+#include "xdgmime.h"
+
+typedef struct _XdgMimeCache XdgMimeCache;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_cache_new_from_file                 XDG_ENTRY(cache_new_from_file)
+#define _xdg_mime_cache_ref                           XDG_ENTRY(cache_ref)
+#define _xdg_mime_cache_unref                         XDG_ENTRY(cache_unref)
+#define _xdg_mime_cache_get_max_buffer_extents        XDG_ENTRY(cache_get_max_buffer_extents)
+#define _xdg_mime_cache_get_mime_type_for_data        XDG_ENTRY(cache_get_mime_type_for_data)
+#define _xdg_mime_cache_get_mime_type_for_file        XDG_ENTRY(cache_get_mime_type_for_file)
+#define _xdg_mime_cache_get_mime_type_from_file_name  XDG_ENTRY(cache_get_mime_type_from_file_name)
+#define _xdg_mime_cache_get_mime_types_from_file_name XDG_ENTRY(cache_get_mime_types_from_file_name)
+#define _xdg_mime_cache_list_mime_parents             XDG_ENTRY(cache_list_mime_parents)
+#define _xdg_mime_cache_mime_type_subclass            XDG_ENTRY(cache_mime_type_subclass)
+#define _xdg_mime_cache_unalias_mime_type             XDG_ENTRY(cache_unalias_mime_type)
+
+#endif
+
+extern XdgMimeCache **_caches;
+
+XdgMimeCache *_xdg_mime_cache_new_from_file (const char   *file_name);
+XdgMimeCache *_xdg_mime_cache_ref           (XdgMimeCache *cache);
+void          _xdg_mime_cache_unref         (XdgMimeCache *cache);
+
+
+const char  *_xdg_mime_cache_get_mime_type_for_data       (const void *data,
+                                                          size_t      len,
+                                                          int        *result_prio);
+const char  *_xdg_mime_cache_get_mime_type_for_file       (const char  *file_name,
+                                                          struct stat *statbuf);
+int          _xdg_mime_cache_get_mime_types_from_file_name (const char *file_name,
+                                                           const char  *mime_types[],
+                                                           int          n_mime_types);
+const char  *_xdg_mime_cache_get_mime_type_from_file_name (const char *file_name);
+int          _xdg_mime_cache_is_valid_mime_type           (const char *mime_type);
+int          _xdg_mime_cache_mime_type_equal              (const char *mime_a,
+                                                          const char *mime_b);
+int          _xdg_mime_cache_media_type_equal             (const char *mime_a,
+                                                          const char *mime_b);
+int          _xdg_mime_cache_mime_type_subclass           (const char *mime_a,
+                                                          const char *mime_b);
+char       **_xdg_mime_cache_list_mime_parents           (const char *mime);
+const char  *_xdg_mime_cache_unalias_mime_type            (const char *mime);
+int          _xdg_mime_cache_get_max_buffer_extents       (void);
+
+#endif /* __XDG_MIME_CACHE_H__ */
diff --git a/gio/xdgmime/xdgmimeglob.c b/gio/xdgmime/xdgmimeglob.c
new file mode 100644 (file)
index 0000000..3aad611
--- /dev/null
@@ -0,0 +1,547 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.c: Private file.  Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeglob.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef struct XdgGlobHashNode XdgGlobHashNode;
+typedef struct XdgGlobList XdgGlobList;
+
+struct XdgGlobHashNode
+{
+  xdg_unichar_t character;
+  const char *mime_type;
+  XdgGlobHashNode *next;
+  XdgGlobHashNode *child;
+};
+struct XdgGlobList
+{
+  const char *data;
+  const char *mime_type;
+  XdgGlobList *next;
+};
+
+struct XdgGlobHash
+{
+  XdgGlobList *literal_list;
+  XdgGlobHashNode *simple_node;
+  XdgGlobList *full_list;
+};
+
+
+/* XdgGlobList
+ */
+static XdgGlobList *
+_xdg_glob_list_new (void)
+{
+  XdgGlobList *new_element;
+
+  new_element = calloc (1, sizeof (XdgGlobList));
+
+  return new_element;
+}
+
+/* Frees glob_list and all of it's children */
+static void
+_xdg_glob_list_free (XdgGlobList *glob_list)
+{
+  XdgGlobList *ptr, *next;
+
+  ptr = glob_list;
+
+  while (ptr != NULL)
+    {
+      next = ptr->next;
+
+      if (ptr->data)
+       free ((void *) ptr->data);
+      if (ptr->mime_type)
+       free ((void *) ptr->mime_type);
+      free (ptr);
+
+      ptr = next;
+    }
+}
+
+static XdgGlobList *
+_xdg_glob_list_append (XdgGlobList *glob_list,
+                      void        *data,
+                      const char  *mime_type)
+{
+  XdgGlobList *new_element;
+  XdgGlobList *tmp_element;
+
+  new_element = _xdg_glob_list_new ();
+  new_element->data = data;
+  new_element->mime_type = mime_type;
+  if (glob_list == NULL)
+    return new_element;
+
+  tmp_element = glob_list;
+  while (tmp_element->next != NULL)
+    tmp_element = tmp_element->next;
+
+  tmp_element->next = new_element;
+
+  return glob_list;
+}
+
+#if 0
+static XdgGlobList *
+_xdg_glob_list_prepend (XdgGlobList *glob_list,
+                       void        *data,
+                       const char  *mime_type)
+{
+  XdgGlobList *new_element;
+
+  new_element = _xdg_glob_list_new ();
+  new_element->data = data;
+  new_element->next = glob_list;
+  new_element->mime_type = mime_type;
+
+  return new_element;
+}
+#endif
+
+/* XdgGlobHashNode
+ */
+
+static XdgGlobHashNode *
+_xdg_glob_hash_node_new (void)
+{
+  XdgGlobHashNode *glob_hash_node;
+
+  glob_hash_node = calloc (1, sizeof (XdgGlobHashNode));
+
+  return glob_hash_node;
+}
+
+static void
+_xdg_glob_hash_node_dump (XdgGlobHashNode *glob_hash_node,
+                         int depth)
+{
+  int i;
+  for (i = 0; i < depth; i++)
+    printf (" ");
+
+  printf ("%c", (char)glob_hash_node->character);
+  if (glob_hash_node->mime_type)
+    printf (" - %s\n", glob_hash_node->mime_type);
+  else
+    printf ("\n");
+  if (glob_hash_node->child)
+    _xdg_glob_hash_node_dump (glob_hash_node->child, depth + 1);
+  if (glob_hash_node->next)
+    _xdg_glob_hash_node_dump (glob_hash_node->next, depth);
+}
+
+static XdgGlobHashNode *
+_xdg_glob_hash_insert_text (XdgGlobHashNode *glob_hash_node,
+                           const char      *text,
+                           const char      *mime_type)
+{
+  XdgGlobHashNode *node;
+  xdg_unichar_t character;
+
+  character = _xdg_utf8_to_ucs4 (text);
+
+  if ((glob_hash_node == NULL) ||
+      (character < glob_hash_node->character))
+    {
+      node = _xdg_glob_hash_node_new ();
+      node->character = character;
+      node->next = glob_hash_node;
+      glob_hash_node = node;
+    }
+  else if (character == glob_hash_node->character)
+    {
+      node = glob_hash_node;
+    }
+  else
+    {
+      XdgGlobHashNode *prev_node;
+      int found_node = FALSE;
+
+      /* Look for the first character of text in glob_hash_node, and insert it if we
+       * have to.*/
+      prev_node = glob_hash_node;
+      node = prev_node->next;
+
+      while (node != NULL)
+       {
+         if (character < node->character)
+           {
+             node = _xdg_glob_hash_node_new ();
+             node->character = character;
+             node->next = prev_node->next;
+             prev_node->next = node;
+
+             found_node = TRUE;
+             break;
+           }
+         else if (character == node->character)
+           {
+             found_node = TRUE;
+             break;
+           }
+         prev_node = node;
+         node = node->next;
+       }
+
+      if (! found_node)
+       {
+         node = _xdg_glob_hash_node_new ();
+         node->character = character;
+         node->next = prev_node->next;
+         prev_node->next = node;
+       }
+    }
+
+  text = _xdg_utf8_next_char (text);
+  if (*text == '\000')
+    {
+      if (node->mime_type)
+       {
+         if (strcmp (node->mime_type, mime_type))
+           {
+             XdgGlobHashNode *child;
+             int found_node = FALSE;
+             
+             child = node->child;
+             while (child && child->character == '\0')
+               {
+                 if (strcmp (child->mime_type, mime_type) == 0)
+                   {
+                     found_node = TRUE;
+                     break;
+                   }
+                 child = child->next;
+               }
+
+             if (!found_node)
+               {
+                 child = _xdg_glob_hash_node_new ();
+                 child->character = '\000';
+                 child->mime_type = strdup (mime_type);
+                 child->child = NULL;
+                 child->next = node->child;
+                 node->child = child;
+               }
+           }
+       }
+      else
+       {
+         node->mime_type = strdup (mime_type);
+       }
+    }
+  else
+    {
+      node->child = _xdg_glob_hash_insert_text (node->child, text, mime_type);
+    }
+  return glob_hash_node;
+}
+
+static int
+_xdg_glob_hash_node_lookup_file_name (XdgGlobHashNode *glob_hash_node,
+                                     const char      *file_name,
+                                     int              ignore_case,
+                                     const char      *mime_types[],
+                                     int              n_mime_types)
+{
+  int n;
+  XdgGlobHashNode *node;
+  xdg_unichar_t character;
+
+  if (glob_hash_node == NULL)
+    return 0;
+
+  character = _xdg_utf8_to_ucs4 (file_name);
+  if (ignore_case)
+    character = _xdg_ucs4_to_lower(character);
+
+  for (node = glob_hash_node; node && character >= node->character; node = node->next)
+    {
+      if (character == node->character)
+       {
+         file_name = _xdg_utf8_next_char (file_name);
+         if (*file_name == '\000')
+           {
+             n = 0;
+              if (node->mime_type)
+               mime_types[n++] = node->mime_type;
+             node = node->child;
+             while (n < n_mime_types && node && node->character == 0)
+               {
+                  if (node->mime_type)
+                   mime_types[n++] = node->mime_type;
+                 node = node->next;
+               }
+           }
+         else
+           {
+             n = _xdg_glob_hash_node_lookup_file_name (node->child,
+                                                       file_name,
+                                                       ignore_case,
+                                                       mime_types,
+                                                       n_mime_types);
+           }
+         return n;
+       }
+    }
+
+  return 0;
+}
+
+int
+_xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+                                const char  *file_name,
+                                const char  *mime_types[],
+                                int          n_mime_types)
+{
+  XdgGlobList *list;
+  const char *ptr;
+  char stopchars[128];
+  int i, n;
+  XdgGlobHashNode *node;
+
+  /* First, check the literals */
+
+  assert (file_name != NULL && n_mime_types > 0);
+
+  for (list = glob_hash->literal_list; list; list = list->next)
+    {
+      if (strcmp ((const char *)list->data, file_name) == 0)
+       {
+         mime_types[0] = list->mime_type;
+         return 1;
+       }
+    }
+
+  i = 0;
+  for (node = glob_hash->simple_node; node; node = node->next)
+    {
+      if (node->character < 128)
+       stopchars[i++] = (char)node->character;
+    }
+  stopchars[i] = '\0';
+  ptr = strpbrk (file_name, stopchars);
+  while (ptr)
+    {
+      n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, FALSE,
+                                               mime_types, n_mime_types);
+      if (n > 0)
+       return n;
+      
+      n = _xdg_glob_hash_node_lookup_file_name (glob_hash->simple_node, ptr, TRUE,
+                                               mime_types, n_mime_types);
+      if (n > 0)
+       return n;
+      
+      ptr = strpbrk (ptr + 1, stopchars);
+    }
+
+  /* FIXME: Not UTF-8 safe */
+  n = 0;
+  for (list = glob_hash->full_list; list && n < n_mime_types; list = list->next)
+    {
+      if (fnmatch ((const char *)list->data, file_name, 0) == 0)
+       mime_types[n++] = list->mime_type;
+    }
+
+  return n;
+}
+
+
+
+/* XdgGlobHash
+ */
+
+XdgGlobHash *
+_xdg_glob_hash_new (void)
+{
+  XdgGlobHash *glob_hash;
+
+  glob_hash = calloc (1, sizeof (XdgGlobHash));
+
+  return glob_hash;
+}
+
+
+static void
+_xdg_glob_hash_free_nodes (XdgGlobHashNode *node)
+{
+  if (node)
+    {
+      if (node->child)
+       _xdg_glob_hash_free_nodes (node->child);
+      if (node->next)
+       _xdg_glob_hash_free_nodes (node->next);
+      if (node->mime_type)
+       free ((void *) node->mime_type);
+      free (node);
+    }
+}
+
+void
+_xdg_glob_hash_free (XdgGlobHash *glob_hash)
+{
+  _xdg_glob_list_free (glob_hash->literal_list);
+  _xdg_glob_list_free (glob_hash->full_list);
+  _xdg_glob_hash_free_nodes (glob_hash->simple_node);
+  free (glob_hash);
+}
+
+XdgGlobType
+_xdg_glob_determine_type (const char *glob)
+{
+  const char *ptr;
+  int maybe_in_simple_glob = FALSE;
+  int first_char = TRUE;
+
+  ptr = glob;
+
+  while (*ptr != '\000')
+    {
+      if (*ptr == '*' && first_char)
+       maybe_in_simple_glob = TRUE;
+      else if (*ptr == '\\' || *ptr == '[' || *ptr == '?' || *ptr == '*')
+         return XDG_GLOB_FULL;
+
+      first_char = FALSE;
+      ptr = _xdg_utf8_next_char (ptr);
+    }
+  if (maybe_in_simple_glob)
+    return XDG_GLOB_SIMPLE;
+  else
+    return XDG_GLOB_LITERAL;
+}
+
+/* glob must be valid UTF-8 */
+void
+_xdg_glob_hash_append_glob (XdgGlobHash *glob_hash,
+                           const char  *glob,
+                           const char  *mime_type)
+{
+  XdgGlobType type;
+
+  assert (glob_hash != NULL);
+  assert (glob != NULL);
+
+  type = _xdg_glob_determine_type (glob);
+
+  switch (type)
+    {
+    case XDG_GLOB_LITERAL:
+      glob_hash->literal_list = _xdg_glob_list_append (glob_hash->literal_list, strdup (glob), strdup (mime_type));
+      break;
+    case XDG_GLOB_SIMPLE:
+      glob_hash->simple_node = _xdg_glob_hash_insert_text (glob_hash->simple_node, glob + 1, mime_type);
+      break;
+    case XDG_GLOB_FULL:
+      glob_hash->full_list = _xdg_glob_list_append (glob_hash->full_list, strdup (glob), strdup (mime_type));
+      break;
+    }
+}
+
+void
+_xdg_glob_hash_dump (XdgGlobHash *glob_hash)
+{
+  XdgGlobList *list;
+  printf ("LITERAL STRINGS\n");
+  if (glob_hash->literal_list == NULL)
+    {
+      printf ("    None\n");
+    }
+  else
+    {
+      for (list = glob_hash->literal_list; list; list = list->next)
+       printf ("    %s - %s\n", (char *)list->data, list->mime_type);
+    }
+  printf ("\nSIMPLE GLOBS\n");
+  _xdg_glob_hash_node_dump (glob_hash->simple_node, 4);
+
+  printf ("\nFULL GLOBS\n");
+  if (glob_hash->full_list == NULL)
+    {
+      printf ("    None\n");
+    }
+  else
+    {
+      for (list = glob_hash->full_list; list; list = list->next)
+       printf ("    %s - %s\n", (char *)list->data, list->mime_type);
+    }
+}
+
+
+void
+_xdg_mime_glob_read_from_file (XdgGlobHash *glob_hash,
+                              const char  *file_name)
+{
+  FILE *glob_file;
+  char line[255];
+
+  glob_file = fopen (file_name, "r");
+
+  if (glob_file == NULL)
+    return;
+
+  /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
+   * Blah */
+  while (fgets (line, 255, glob_file) != NULL)
+    {
+      char *colon;
+      if (line[0] == '#')
+       continue;
+
+      colon = strchr (line, ':');
+      if (colon == NULL)
+       continue;
+      *(colon++) = '\000';
+      colon[strlen (colon) -1] = '\000';
+      _xdg_glob_hash_append_glob (glob_hash, colon, line);
+    }
+
+  fclose (glob_file);
+}
diff --git a/gio/xdgmime/xdgmimeglob.h b/gio/xdgmime/xdgmimeglob.h
new file mode 100644 (file)
index 0000000..25a1f20
--- /dev/null
@@ -0,0 +1,67 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeglob.h: Private file.  Datastructure for storing the globs.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_GLOB_H__
+#define __XDG_MIME_GLOB_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgGlobHash XdgGlobHash;
+
+typedef enum
+{
+  XDG_GLOB_LITERAL, /* Makefile */
+  XDG_GLOB_SIMPLE,  /* *.gif */
+  XDG_GLOB_FULL     /* x*.[ch] */
+} XdgGlobType;
+
+  
+#ifdef XDG_PREFIX
+#define _xdg_mime_glob_read_from_file         XDG_ENTRY(glob_read_from_file)
+#define _xdg_glob_hash_new                    XDG_ENTRY(hash_new)
+#define _xdg_glob_hash_free                   XDG_ENTRY(hash_free)
+#define _xdg_glob_hash_lookup_file_name       XDG_ENTRY(hash_lookup_file_name)
+#define _xdg_glob_hash_append_glob            XDG_ENTRY(hash_append_glob)
+#define _xdg_glob_determine_type              XDG_ENTRY(determine_type)
+#define _xdg_glob_hash_dump                   XDG_ENTRY(hash_dump)
+#endif
+
+void         _xdg_mime_glob_read_from_file   (XdgGlobHash *glob_hash,
+                                             const char  *file_name);
+XdgGlobHash *_xdg_glob_hash_new              (void);
+void         _xdg_glob_hash_free             (XdgGlobHash *glob_hash);
+int          _xdg_glob_hash_lookup_file_name (XdgGlobHash *glob_hash,
+                                             const char  *text,
+                                             const char  *mime_types[],
+                                             int          n_mime_types);
+void         _xdg_glob_hash_append_glob      (XdgGlobHash *glob_hash,
+                                             const char  *glob,
+                                             const char  *mime_type);
+XdgGlobType  _xdg_glob_determine_type        (const char  *glob);
+void         _xdg_glob_hash_dump             (XdgGlobHash *glob_hash);
+
+#endif /* __XDG_MIME_GLOB_H__ */
diff --git a/gio/xdgmime/xdgmimeint.c b/gio/xdgmime/xdgmimeint.c
new file mode 100644 (file)
index 0000000..4a0ac4c
--- /dev/null
@@ -0,0 +1,154 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.c: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeint.h"
+#include <ctype.h>
+#include <string.h>
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+static const char _xdg_utf8_skip_data[256] = {
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+  3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,1,1
+};
+
+const char * const _xdg_utf8_skip = _xdg_utf8_skip_data;
+
+
+
+/* Returns the number of unprocessed characters. */
+xdg_unichar_t
+_xdg_utf8_to_ucs4(const char *source)
+{
+  xdg_unichar_t ucs32;
+  if( ! ( *source & 0x80 ) )
+    {
+      ucs32 = *source;
+    }
+  else
+    {
+      int bytelength = 0;
+      xdg_unichar_t result;
+      if ( ! (*source & 0x40) )
+       {
+         ucs32 = *source;
+       }
+      else
+       {
+         if ( ! (*source & 0x20) )
+           {
+             result = *source++ & 0x1F;
+             bytelength = 2;
+           }
+         else if ( ! (*source & 0x10) )
+           {
+             result = *source++ & 0x0F;
+             bytelength = 3;
+           }
+         else if ( ! (*source & 0x08) )
+           {
+             result = *source++ & 0x07;
+             bytelength = 4;
+           }
+         else if ( ! (*source & 0x04) )
+           {
+             result = *source++ & 0x03;
+             bytelength = 5;
+           }
+         else if ( ! (*source & 0x02) )
+           {
+             result = *source++ & 0x01;
+             bytelength = 6;
+           }
+         else
+           {
+             result = *source++;
+             bytelength = 1;
+           }
+
+         for ( bytelength --; bytelength > 0; bytelength -- )
+           {
+             result <<= 6;
+             result |= *source++ & 0x3F;
+           }
+         ucs32 = result;
+       }
+    }
+  return ucs32;
+}
+
+
+/* hullo.  this is great code.  don't rewrite it */
+
+xdg_unichar_t
+_xdg_ucs4_to_lower (xdg_unichar_t source)
+{
+  /* FIXME: Do a real to_upper sometime */
+  /* CaseFolding-3.2.0.txt has a table of rules. */
+  if ((source & 0xFF) == source)
+    return (xdg_unichar_t) tolower ((unsigned char) source);
+  return source;
+}
+
+int
+_xdg_utf8_validate (const char *source)
+{
+  /* FIXME: actually write */
+  return TRUE;
+}
+
+const char *
+_xdg_get_base_name (const char *file_name)
+{
+  const char *base_name;
+
+  if (file_name == NULL)
+    return NULL;
+
+  base_name = strrchr (file_name, '/');
+
+  if (base_name == NULL)
+    return file_name;
+  else
+    return base_name + 1;
+}
diff --git a/gio/xdgmime/xdgmimeint.h b/gio/xdgmime/xdgmimeint.h
new file mode 100644 (file)
index 0000000..2881487
--- /dev/null
@@ -0,0 +1,73 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeint.h: Internal defines and functions.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_INT_H__
+#define __XDG_MIME_INT_H__
+
+#include "xdgmime.h"
+
+
+#ifndef        FALSE
+#define        FALSE (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE (!FALSE)
+#endif
+
+/* FIXME: Needs to be configure check */
+typedef unsigned int   xdg_unichar_t;
+typedef unsigned char  xdg_uchar8_t;
+typedef unsigned short xdg_uint16_t;
+typedef unsigned int   xdg_uint32_t;
+
+#ifdef XDG_PREFIX
+#define _xdg_utf8_skip   XDG_ENTRY(utf8_skip)
+#define _xdg_utf8_to_ucs4   XDG_ENTRY(utf8_to_ucs4)
+#define _xdg_ucs4_to_lower   XDG_ENTRY(ucs4_to_lower)
+#define _xdg_utf8_validate   XDG_ENTRY(utf8_validate)
+#define _xdg_get_base_name   XDG_ENTRY(get_ase_name)
+#endif
+
+#define SWAP_BE16_TO_LE16(val) (xdg_uint16_t)(((xdg_uint16_t)(val) << 8)|((xdg_uint16_t)(val) >> 8))
+
+#define SWAP_BE32_TO_LE32(val) (xdg_uint32_t)((((xdg_uint32_t)(val) & 0xFF000000U) >> 24) |    \
+                                             (((xdg_uint32_t)(val) & 0x00FF0000U) >> 8) |      \
+                                             (((xdg_uint32_t)(val) & 0x0000FF00U) << 8) |      \
+                                             (((xdg_uint32_t)(val) & 0x000000FFU) << 24))
+/* UTF-8 utils
+ */
+extern const char *const _xdg_utf8_skip;
+#define _xdg_utf8_next_char(p) (char *)((p) + _xdg_utf8_skip[*(unsigned char *)(p)])
+#define _xdg_utf8_char_size(p) (int) (_xdg_utf8_skip[*(unsigned char *)(p)])
+
+xdg_unichar_t  _xdg_utf8_to_ucs4  (const char    *source);
+xdg_unichar_t  _xdg_ucs4_to_lower (xdg_unichar_t  source);
+int            _xdg_utf8_validate (const char    *source);
+const char    *_xdg_get_base_name (const char    *file_name);
+
+#endif /* __XDG_MIME_INT_H__ */
diff --git a/gio/xdgmime/xdgmimemagic.c b/gio/xdgmime/xdgmimemagic.c
new file mode 100644 (file)
index 0000000..a2320f5
--- /dev/null
@@ -0,0 +1,813 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.: Private file.  Datastructure for storing magic files.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <assert.h>
+#include "xdgmimemagic.h"
+#include "xdgmimeint.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+#if !defined getc_unlocked && !defined HAVE_GETC_UNLOCKED
+# define getc_unlocked(fp) getc (fp)
+#endif
+
+typedef struct XdgMimeMagicMatch XdgMimeMagicMatch;
+typedef struct XdgMimeMagicMatchlet XdgMimeMagicMatchlet;
+
+typedef enum
+{
+  XDG_MIME_MAGIC_SECTION,
+  XDG_MIME_MAGIC_MAGIC,
+  XDG_MIME_MAGIC_ERROR,
+  XDG_MIME_MAGIC_EOF
+} XdgMimeMagicState;
+
+struct XdgMimeMagicMatch
+{
+  const char *mime_type;
+  int priority;
+  XdgMimeMagicMatchlet *matchlet;
+  XdgMimeMagicMatch *next;
+};
+
+
+struct XdgMimeMagicMatchlet
+{
+  int indent;
+  int offset;
+  unsigned int value_length;
+  unsigned char *value;
+  unsigned char *mask;
+  unsigned int range_length;
+  unsigned int word_size;
+  XdgMimeMagicMatchlet *next;
+};
+
+
+struct XdgMimeMagic
+{
+  XdgMimeMagicMatch *match_list;
+  int max_extent;
+};
+
+static XdgMimeMagicMatch *
+_xdg_mime_magic_match_new (void)
+{
+  return calloc (1, sizeof (XdgMimeMagicMatch));
+}
+
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_new (void)
+{
+  XdgMimeMagicMatchlet *matchlet;
+
+  matchlet = malloc (sizeof (XdgMimeMagicMatchlet));
+
+  matchlet->indent = 0;
+  matchlet->offset = 0;
+  matchlet->value_length = 0;
+  matchlet->value = NULL;
+  matchlet->mask = NULL;
+  matchlet->range_length = 1;
+  matchlet->word_size = 1;
+  matchlet->next = NULL;
+
+  return matchlet;
+}
+
+
+static void
+_xdg_mime_magic_matchlet_free (XdgMimeMagicMatchlet *mime_magic_matchlet)
+{
+  if (mime_magic_matchlet)
+    {
+      if (mime_magic_matchlet->next)
+       _xdg_mime_magic_matchlet_free (mime_magic_matchlet->next);
+      if (mime_magic_matchlet->value)
+       free (mime_magic_matchlet->value);
+      if (mime_magic_matchlet->mask)
+       free (mime_magic_matchlet->mask);
+      free (mime_magic_matchlet);
+    }
+}
+
+
+/* Frees mime_magic_match and the remainder of its list
+ */
+static void
+_xdg_mime_magic_match_free (XdgMimeMagicMatch *mime_magic_match)
+{
+  XdgMimeMagicMatch *ptr, *next;
+
+  ptr = mime_magic_match;
+  while (ptr)
+    {
+      next = ptr->next;
+
+      if (ptr->mime_type)
+       free ((void *) ptr->mime_type);
+      if (ptr->matchlet)
+       _xdg_mime_magic_matchlet_free (ptr->matchlet);
+      free (ptr);
+
+      ptr = next;
+    }
+}
+
+/* Reads in a hunk of data until a newline character or a '\000' is hit.  The
+ * returned string is null terminated, and doesn't include the newline.
+ */
+static unsigned char *
+_xdg_mime_magic_read_to_newline (FILE *magic_file,
+                                int  *end_of_file)
+{
+  unsigned char *retval;
+  int c;
+  int len, pos;
+
+  len = 128;
+  pos = 0;
+  retval = malloc (len);
+  *end_of_file = FALSE;
+
+  while (TRUE)
+    {
+      c = getc_unlocked (magic_file);
+      if (c == EOF)
+       {
+         *end_of_file = TRUE;
+         break;
+       }
+      if (c == '\n' || c == '\000')
+       break;
+      retval[pos++] = (unsigned char) c;
+      if (pos % 128 == 127)
+       {
+         len = len + 128;
+         retval = realloc (retval, len);
+       }
+    }
+
+  retval[pos] = '\000';
+  return retval;
+}
+
+/* Returns the number read from the file, or -1 if no number could be read.
+ */
+static int
+_xdg_mime_magic_read_a_number (FILE *magic_file,
+                              int  *end_of_file)
+{
+  /* LONG_MAX is about 20 characters on my system */
+#define MAX_NUMBER_SIZE 30
+  char number_string[MAX_NUMBER_SIZE + 1];
+  int pos = 0;
+  int c;
+  long retval = -1;
+
+  while (TRUE)
+    {
+      c = getc_unlocked (magic_file);
+
+      if (c == EOF)
+       {
+         *end_of_file = TRUE;
+         break;
+       }
+      if (! isdigit (c))
+       {
+         ungetc (c, magic_file);
+         break;
+       }
+      number_string[pos] = (char) c;
+      pos++;
+      if (pos == MAX_NUMBER_SIZE)
+       break;
+    }
+  if (pos > 0)
+    {
+      number_string[pos] = '\000';
+      errno = 0;
+      retval = strtol (number_string, NULL, 10);
+
+      if ((retval < INT_MIN) || (retval > INT_MAX) || (errno != 0))
+       return -1;
+    }
+
+  return retval;
+}
+
+/* Headers are of the format:
+ * [<priority>:<mime-type>]
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_header (FILE *magic_file, XdgMimeMagicMatch *match)
+{
+  int c;
+  char *buffer;
+  char *end_ptr;
+  int end_of_file = 0;
+
+  assert (magic_file != NULL);
+  assert (match != NULL);
+
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+  if (c != '[')
+    return XDG_MIME_MAGIC_ERROR;
+
+  match->priority = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+  if (end_of_file)
+    return XDG_MIME_MAGIC_EOF;
+  if (match->priority == -1)
+    return XDG_MIME_MAGIC_ERROR;
+
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+  if (c != ':')
+    return XDG_MIME_MAGIC_ERROR;
+
+  buffer = (char *)_xdg_mime_magic_read_to_newline (magic_file, &end_of_file);
+  if (end_of_file)
+    return XDG_MIME_MAGIC_EOF;
+
+  end_ptr = buffer;
+  while (*end_ptr != ']' && *end_ptr != '\000' && *end_ptr != '\n')
+    end_ptr++;
+  if (*end_ptr != ']')
+    {
+      free (buffer);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+  *end_ptr = '\000';
+
+  match->mime_type = strdup (buffer);
+  free (buffer);
+
+  return XDG_MIME_MAGIC_MAGIC;
+}
+
+static XdgMimeMagicState
+_xdg_mime_magic_parse_error (FILE *magic_file)
+{
+  int c;
+
+  while (1)
+    {
+      c = getc_unlocked (magic_file);
+      if (c == EOF)
+       return XDG_MIME_MAGIC_EOF;
+      if (c == '\n')
+       return XDG_MIME_MAGIC_SECTION;
+    }
+}
+
+/* Headers are of the format:
+ * [ indent ] ">" start-offset "=" value
+ * [ "&" mask ] [ "~" word-size ] [ "+" range-length ] "\n"
+ */
+static XdgMimeMagicState
+_xdg_mime_magic_parse_magic_line (FILE              *magic_file,
+                                 XdgMimeMagicMatch *match)
+{
+  XdgMimeMagicMatchlet *matchlet;
+  int c;
+  int end_of_file;
+  int indent = 0;
+  int bytes_read;
+
+  assert (magic_file != NULL);
+
+  /* Sniff the buffer to make sure it's a valid line */
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+  else if (c == '[')
+    {
+      ungetc (c, magic_file);
+      return XDG_MIME_MAGIC_SECTION;
+    }
+  else if (c == '\n')
+    return XDG_MIME_MAGIC_MAGIC;
+
+  /* At this point, it must be a digit or a '>' */
+  end_of_file = FALSE;
+  if (isdigit (c))
+    {
+      ungetc (c, magic_file);
+      indent = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+      if (end_of_file)
+       return XDG_MIME_MAGIC_EOF;
+      if (indent == -1)
+       return XDG_MIME_MAGIC_ERROR;
+      c = getc_unlocked (magic_file);
+      if (c == EOF)
+       return XDG_MIME_MAGIC_EOF;
+    }
+
+  if (c != '>')
+    return XDG_MIME_MAGIC_ERROR;
+
+  matchlet = _xdg_mime_magic_matchlet_new ();
+  matchlet->indent = indent;
+  matchlet->offset = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+  if (end_of_file)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  if (matchlet->offset == -1)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  else if (c != '=')
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+
+  /* Next two bytes determine how long the value is */
+  matchlet->value_length = 0;
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  matchlet->value_length = c & 0xFF;
+  matchlet->value_length = matchlet->value_length << 8;
+
+  c = getc_unlocked (magic_file);
+  if (c == EOF)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_EOF;
+    }
+  matchlet->value_length = matchlet->value_length + (c & 0xFF);
+
+  matchlet->value = malloc (matchlet->value_length);
+
+  /* OOM */
+  if (matchlet->value == NULL)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      return XDG_MIME_MAGIC_ERROR;
+    }
+  bytes_read = fread (matchlet->value, 1, matchlet->value_length, magic_file);
+  if (bytes_read != matchlet->value_length)
+    {
+      _xdg_mime_magic_matchlet_free (matchlet);
+      if (feof (magic_file))
+       return XDG_MIME_MAGIC_EOF;
+      else
+       return XDG_MIME_MAGIC_ERROR;
+    }
+
+  c = getc_unlocked (magic_file);
+  if (c == '&')
+    {
+      matchlet->mask = malloc (matchlet->value_length);
+      /* OOM */
+      if (matchlet->mask == NULL)
+       {
+         _xdg_mime_magic_matchlet_free (matchlet);
+         return XDG_MIME_MAGIC_ERROR;
+       }
+      bytes_read = fread (matchlet->mask, 1, matchlet->value_length, magic_file);
+      if (bytes_read != matchlet->value_length)
+       {
+         _xdg_mime_magic_matchlet_free (matchlet);
+         if (feof (magic_file))
+           return XDG_MIME_MAGIC_EOF;
+         else
+           return XDG_MIME_MAGIC_ERROR;
+       }
+      c = getc_unlocked (magic_file);
+    }
+
+  if (c == '~')
+    {
+      matchlet->word_size = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+      if (end_of_file)
+       {
+         _xdg_mime_magic_matchlet_free (matchlet);
+         return XDG_MIME_MAGIC_EOF;
+       }
+      if (matchlet->word_size != 0 &&
+         matchlet->word_size != 1 &&
+         matchlet->word_size != 2 &&
+         matchlet->word_size != 4)
+       {
+         _xdg_mime_magic_matchlet_free (matchlet);
+         return XDG_MIME_MAGIC_ERROR;
+       }
+      c = getc_unlocked (magic_file);
+    }
+
+  if (c == '+')
+    {
+      matchlet->range_length = _xdg_mime_magic_read_a_number (magic_file, &end_of_file);
+      if (end_of_file)
+       {
+         _xdg_mime_magic_matchlet_free (matchlet);
+         return XDG_MIME_MAGIC_EOF;
+       }
+      if (matchlet->range_length == -1)
+       {
+         _xdg_mime_magic_matchlet_free (matchlet);
+         return XDG_MIME_MAGIC_ERROR;
+       }
+      c = getc_unlocked (magic_file);
+    }
+
+
+  if (c == '\n')
+    {
+      /* We clean up the matchlet, byte swapping if needed */
+      if (matchlet->word_size > 1)
+       {
+         int i;
+         if (matchlet->value_length % matchlet->word_size != 0)
+           {
+             _xdg_mime_magic_matchlet_free (matchlet);
+             return XDG_MIME_MAGIC_ERROR;
+           }
+         /* FIXME: need to get this defined in a <config.h> style file */
+#if LITTLE_ENDIAN
+         for (i = 0; i < matchlet->value_length; i = i + matchlet->word_size)
+           {
+             if (matchlet->word_size == 2)
+               *((xdg_uint16_t *) matchlet->value + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->value + i)));
+             else if (matchlet->word_size == 4)
+               *((xdg_uint32_t *) matchlet->value + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->value + i)));
+             if (matchlet->mask)
+               {
+                 if (matchlet->word_size == 2)
+                   *((xdg_uint16_t *) matchlet->mask + i) = SWAP_BE16_TO_LE16 (*((xdg_uint16_t *) (matchlet->mask + i)));
+                 else if (matchlet->word_size == 4)
+                   *((xdg_uint32_t *) matchlet->mask + i) = SWAP_BE32_TO_LE32 (*((xdg_uint32_t *) (matchlet->mask + i)));
+
+               }
+           }
+#endif
+       }
+
+      matchlet->next = match->matchlet;
+      match->matchlet = matchlet;
+
+
+      return XDG_MIME_MAGIC_MAGIC;
+    }
+
+  _xdg_mime_magic_matchlet_free (matchlet);
+  if (c == EOF)
+    return XDG_MIME_MAGIC_EOF;
+
+  return XDG_MIME_MAGIC_ERROR;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_to_data (XdgMimeMagicMatchlet *matchlet,
+                                         const void           *data,
+                                         size_t                len)
+{
+  int i, j;
+  for (i = matchlet->offset; i < matchlet->offset + matchlet->range_length; i++)
+    {
+      int valid_matchlet = TRUE;
+
+      if (i + matchlet->value_length > len)
+       return FALSE;
+
+      if (matchlet->mask)
+       {
+         for (j = 0; j < matchlet->value_length; j++)
+           {
+             if ((matchlet->value[j] & matchlet->mask[j]) !=
+                 ((((unsigned char *) data)[j + i]) & matchlet->mask[j]))
+               {
+                 valid_matchlet = FALSE;
+                 break;
+               }
+           }
+       }
+      else
+       {
+         for (j = 0; j <  matchlet->value_length; j++)
+           {
+             if (matchlet->value[j] != ((unsigned char *) data)[j + i])
+               {
+                 valid_matchlet = FALSE;
+                 break;
+               }
+           }
+       }
+      if (valid_matchlet)
+       return TRUE;
+    }
+  return FALSE;
+}
+
+static int
+_xdg_mime_magic_matchlet_compare_level (XdgMimeMagicMatchlet *matchlet,
+                                       const void           *data,
+                                       size_t                len,
+                                       int                   indent)
+{
+  while ((matchlet != NULL) && (matchlet->indent == indent))
+    {
+      if (_xdg_mime_magic_matchlet_compare_to_data (matchlet, data, len))
+       {
+         if ((matchlet->next == NULL) ||
+             (matchlet->next->indent <= indent))
+           return TRUE;
+
+         if (_xdg_mime_magic_matchlet_compare_level (matchlet->next,
+                                                     data,
+                                                     len,
+                                                     indent + 1))
+           return TRUE;
+       }
+
+      do
+       {
+         matchlet = matchlet->next;
+       }
+      while (matchlet && matchlet->indent > indent);
+    }
+
+  return FALSE;
+}
+
+static int
+_xdg_mime_magic_match_compare_to_data (XdgMimeMagicMatch *match,
+                                      const void        *data,
+                                      size_t             len)
+{
+  return _xdg_mime_magic_matchlet_compare_level (match->matchlet, data, len, 0);
+}
+
+static void
+_xdg_mime_magic_insert_match (XdgMimeMagic      *mime_magic,
+                             XdgMimeMagicMatch *match)
+{
+  XdgMimeMagicMatch *list;
+
+  if (mime_magic->match_list == NULL)
+    {
+      mime_magic->match_list = match;
+      return;
+    }
+
+  if (match->priority > mime_magic->match_list->priority)
+    {
+      match->next = mime_magic->match_list;
+      mime_magic->match_list = match;
+      return;
+    }
+
+  list = mime_magic->match_list;
+  while (list->next != NULL)
+    {
+      if (list->next->priority < match->priority)
+       {
+         match->next = list->next;
+         list->next = match;
+         return;
+       }
+      list = list->next;
+    }
+  list->next = match;
+  match->next = NULL;
+}
+
+XdgMimeMagic *
+_xdg_mime_magic_new (void)
+{
+  return calloc (1, sizeof (XdgMimeMagic));
+}
+
+void
+_xdg_mime_magic_free (XdgMimeMagic *mime_magic)
+{
+  if (mime_magic) {
+    _xdg_mime_magic_match_free (mime_magic->match_list);
+    free (mime_magic);
+  }
+}
+
+int
+_xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic)
+{
+  return mime_magic->max_extent;
+}
+
+const char *
+_xdg_mime_magic_lookup_data (XdgMimeMagic *mime_magic,
+                            const void   *data,
+                            size_t        len,
+                            int           *result_prio,
+                             const char   *mime_types[],
+                             int           n_mime_types)
+{
+  XdgMimeMagicMatch *match;
+  const char *mime_type;
+  int n;
+  int prio;
+
+  prio = 0;
+  mime_type = NULL;
+  for (match = mime_magic->match_list; match; match = match->next)
+    {
+      if (_xdg_mime_magic_match_compare_to_data (match, data, len))
+       {
+         prio = match->priority;
+         mime_type = match->mime_type;
+         break;
+       }
+      else 
+       {
+         for (n = 0; n < n_mime_types; n++)
+           {
+             if (mime_types[n] && 
+                 _xdg_mime_mime_type_equal (mime_types[n], match->mime_type))
+               mime_types[n] = NULL;
+           }
+       }
+    }
+
+  if (mime_type == NULL)
+    {
+      for (n = 0; n < n_mime_types; n++)
+       {
+         if (mime_types[n])
+           mime_type = mime_types[n];
+       }
+    }
+  
+  if (result_prio)
+    *result_prio = prio;
+
+  return mime_type;
+}
+
+static void
+_xdg_mime_update_mime_magic_extents (XdgMimeMagic *mime_magic)
+{
+  XdgMimeMagicMatch *match;
+  int max_extent = 0;
+
+  for (match = mime_magic->match_list; match; match = match->next)
+    {
+      XdgMimeMagicMatchlet *matchlet;
+
+      for (matchlet = match->matchlet; matchlet; matchlet = matchlet->next)
+       {
+         int extent;
+
+         extent = matchlet->value_length + matchlet->offset + matchlet->range_length;
+         if (max_extent < extent)
+           max_extent = extent;
+       }
+    }
+
+  mime_magic->max_extent = max_extent;
+}
+
+static XdgMimeMagicMatchlet *
+_xdg_mime_magic_matchlet_mirror (XdgMimeMagicMatchlet *matchlets)
+{
+  XdgMimeMagicMatchlet *new_list;
+  XdgMimeMagicMatchlet *tmp;
+
+  if ((matchlets == NULL) || (matchlets->next == NULL))
+    return matchlets;
+
+  new_list = NULL;
+  tmp = matchlets;
+  while (tmp != NULL)
+    {
+      XdgMimeMagicMatchlet *matchlet;
+
+      matchlet = tmp;
+      tmp = tmp->next;
+      matchlet->next = new_list;
+      new_list = matchlet;
+    }
+
+  return new_list;
+
+}
+
+static void
+_xdg_mime_magic_read_magic_file (XdgMimeMagic *mime_magic,
+                                FILE         *magic_file)
+{
+  XdgMimeMagicState state;
+  XdgMimeMagicMatch *match = NULL; /* Quiet compiler */
+
+  state = XDG_MIME_MAGIC_SECTION;
+
+  while (state != XDG_MIME_MAGIC_EOF)
+    {
+      switch (state)
+       {
+       case XDG_MIME_MAGIC_SECTION:
+         match = _xdg_mime_magic_match_new ();
+         state = _xdg_mime_magic_parse_header (magic_file, match);
+         if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+           _xdg_mime_magic_match_free (match);
+         break;
+       case XDG_MIME_MAGIC_MAGIC:
+         state = _xdg_mime_magic_parse_magic_line (magic_file, match);
+         if (state == XDG_MIME_MAGIC_SECTION ||
+             (state == XDG_MIME_MAGIC_EOF && match->mime_type))
+           {
+             match->matchlet = _xdg_mime_magic_matchlet_mirror (match->matchlet);
+             _xdg_mime_magic_insert_match (mime_magic, match);
+           }
+         else if (state == XDG_MIME_MAGIC_EOF || state == XDG_MIME_MAGIC_ERROR)
+           _xdg_mime_magic_match_free (match);
+         break;
+       case XDG_MIME_MAGIC_ERROR:
+         state = _xdg_mime_magic_parse_error (magic_file);
+         break;
+       case XDG_MIME_MAGIC_EOF:
+       default:
+         /* Make the compiler happy */
+         assert (0);
+       }
+    }
+  _xdg_mime_update_mime_magic_extents (mime_magic);
+}
+
+void
+_xdg_mime_magic_read_from_file (XdgMimeMagic *mime_magic,
+                               const char   *file_name)
+{
+  FILE *magic_file;
+  char header[12];
+
+  magic_file = fopen (file_name, "r");
+
+  if (magic_file == NULL)
+    return;
+
+  if (fread (header, 1, 12, magic_file) == 12)
+    {
+      if (memcmp ("MIME-Magic\0\n", header, 12) == 0)
+        _xdg_mime_magic_read_magic_file (mime_magic, magic_file);
+    }
+
+  fclose (magic_file);
+}
diff --git a/gio/xdgmime/xdgmimemagic.h b/gio/xdgmime/xdgmimemagic.h
new file mode 100644 (file)
index 0000000..8f11305
--- /dev/null
@@ -0,0 +1,57 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimemagic.h: Private file.  Datastructure for storing the magic files.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2003  Red Hat, Inc.
+ * Copyright (C) 2003  Jonathan Blandford <jrb@alum.mit.edu>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_MAGIC_H__
+#define __XDG_MIME_MAGIC_H__
+
+#include <unistd.h>
+#include "xdgmime.h"
+typedef struct XdgMimeMagic XdgMimeMagic;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_glob_read_from_file             XDG_ENTRY(glob_read_from_file)
+#define _xdg_mime_magic_new                       XDG_ENTRY(magic_new)
+#define _xdg_mime_magic_read_from_file            XDG_ENTRY(magic_read_from_file)
+#define _xdg_mime_magic_free                      XDG_ENTRY(magic_free)
+#define _xdg_mime_magic_get_buffer_extents        XDG_ENTRY(magic_get_buffer_extents)
+#define _xdg_mime_magic_lookup_data               XDG_ENTRY(magic_lookup_data)
+#endif
+
+
+XdgMimeMagic *_xdg_mime_magic_new                (void);
+void          _xdg_mime_magic_read_from_file     (XdgMimeMagic *mime_magic,
+                                                 const char   *file_name);
+void          _xdg_mime_magic_free               (XdgMimeMagic *mime_magic);
+int           _xdg_mime_magic_get_buffer_extents (XdgMimeMagic *mime_magic);
+const char   *_xdg_mime_magic_lookup_data        (XdgMimeMagic *mime_magic,
+                                                 const void   *data,
+                                                 size_t        len,
+                                                 int          *result_prio,
+                                                 const char   *mime_types[],
+                                                 int           n_mime_types);
+
+#endif /* __XDG_MIME_MAGIC_H__ */
diff --git a/gio/xdgmime/xdgmimeparent.c b/gio/xdgmime/xdgmimeparent.c
new file mode 100644 (file)
index 0000000..511bbac
--- /dev/null
@@ -0,0 +1,219 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimealias.c: Private file.  Datastructure for storing the hierarchy.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2004  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xdgmimeparent.h"
+#include "xdgmimeint.h"
+#include <stdlib.h>
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <fnmatch.h>
+
+#ifndef        FALSE
+#define        FALSE   (0)
+#endif
+
+#ifndef        TRUE
+#define        TRUE    (!FALSE)
+#endif
+
+typedef struct XdgMimeParents XdgMimeParents;
+
+struct XdgMimeParents
+{
+  char *mime;
+  char **parents;
+  int n_parents;
+};
+
+struct XdgParentList
+{
+  struct XdgMimeParents *parents;
+  int n_mimes;
+};
+
+XdgParentList *
+_xdg_mime_parent_list_new (void)
+{
+  XdgParentList *list;
+
+  list = malloc (sizeof (XdgParentList));
+
+  list->parents = NULL;
+  list->n_mimes = 0;
+
+  return list;
+}
+
+void         
+_xdg_mime_parent_list_free (XdgParentList *list)
+{
+  int i;
+  char **p;
+
+  if (list->parents)
+    {
+      for (i = 0; i < list->n_mimes; i++)
+       {
+         for (p = list->parents[i].parents; *p; p++)
+           free (*p);
+
+         free (list->parents[i].parents);
+         free (list->parents[i].mime);
+       }
+      free (list->parents);
+    }
+  free (list);
+}
+
+static int
+parent_entry_cmp (const void *v1, const void *v2)
+{
+  return strcmp (((XdgMimeParents *)v1)->mime, ((XdgMimeParents *)v2)->mime);
+}
+
+const char **
+_xdg_mime_parent_list_lookup (XdgParentList *list,
+                             const char    *mime)
+{
+  XdgMimeParents *entry;
+  XdgMimeParents key;
+
+  if (list->n_mimes > 0)
+    {
+      key.mime = (char *)mime;
+      key.parents = NULL;
+
+      entry = bsearch (&key, list->parents, list->n_mimes,
+                      sizeof (XdgMimeParents), &parent_entry_cmp);
+      if (entry)
+        return (const char **)entry->parents;
+    }
+
+  return NULL;
+}
+
+void
+_xdg_mime_parent_read_from_file (XdgParentList *list,
+                                const char    *file_name)
+{
+  FILE *file;
+  char line[255];
+  int i, alloc;
+  XdgMimeParents *entry;
+
+  file = fopen (file_name, "r");
+
+  if (file == NULL)
+    return;
+
+  /* FIXME: Not UTF-8 safe.  Doesn't work if lines are greater than 255 chars.
+   * Blah */
+  alloc = list->n_mimes + 16;
+  list->parents = realloc (list->parents, alloc * sizeof (XdgMimeParents));
+  while (fgets (line, 255, file) != NULL)
+    {
+      char *sep;
+      if (line[0] == '#')
+       continue;
+
+      sep = strchr (line, ' ');
+      if (sep == NULL)
+       continue;
+      *(sep++) = '\000';
+      sep[strlen (sep) -1] = '\000';
+      entry = NULL;
+      for (i = 0; i < list->n_mimes; i++)
+       {
+         if (strcmp (list->parents[i].mime, line) == 0)
+           {
+             entry = &(list->parents[i]);
+             break;
+           }
+       }
+      
+      if (!entry)
+       {
+         if (list->n_mimes == alloc)
+           {
+             alloc <<= 1;
+             list->parents = realloc (list->parents, 
+                                      alloc * sizeof (XdgMimeParents));
+           }
+         list->parents[list->n_mimes].mime = strdup (line);
+         list->parents[list->n_mimes].parents = NULL;
+         entry = &(list->parents[list->n_mimes]);
+         list->n_mimes++;
+       }
+
+      if (!entry->parents)
+       {
+         entry->n_parents = 1;
+         entry->parents = malloc ((entry->n_parents + 1) * sizeof (char *));
+       }
+      else
+       {
+         entry->n_parents += 1;
+         entry->parents = realloc (entry->parents, 
+                                   (entry->n_parents + 2) * sizeof (char *));
+       }
+      entry->parents[entry->n_parents - 1] = strdup (sep);
+      entry->parents[entry->n_parents] = NULL;
+    }
+
+  list->parents = realloc (list->parents, 
+                          list->n_mimes * sizeof (XdgMimeParents));
+
+  fclose (file);  
+  
+  if (list->n_mimes > 1)
+    qsort (list->parents, list->n_mimes, 
+           sizeof (XdgMimeParents), &parent_entry_cmp);
+}
+
+
+void         
+_xdg_mime_parent_list_dump (XdgParentList *list)
+{
+  int i;
+  char **p;
+
+  if (list->parents)
+    {
+      for (i = 0; i < list->n_mimes; i++)
+       {
+         for (p = list->parents[i].parents; *p; p++)
+           printf ("%s %s\n", list->parents[i].mime, *p);
+       }
+    }
+}
+
+
diff --git a/gio/xdgmime/xdgmimeparent.h b/gio/xdgmime/xdgmimeparent.h
new file mode 100644 (file)
index 0000000..257ea04
--- /dev/null
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* xdgmimeparent.h: Private file.  Datastructure for storing the hierarchy.
+ *
+ * More info can be found at http://www.freedesktop.org/standards/
+ *
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 200  Matthias Clasen <mclasen@redhat.com>
+ *
+ * Licensed under the Academic Free License version 2.0
+ * Or under the following terms:
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.         See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __XDG_MIME_PARENT_H__
+#define __XDG_MIME_PARENT_H__
+
+#include "xdgmime.h"
+
+typedef struct XdgParentList XdgParentList;
+
+#ifdef XDG_PREFIX
+#define _xdg_mime_parent_read_from_file        XDG_ENTRY(parent_read_from_file)
+#define _xdg_mime_parent_list_new              XDG_ENTRY(parent_list_new)
+#define _xdg_mime_parent_list_free             XDG_ENTRY(parent_list_free)
+#define _xdg_mime_parent_list_lookup           XDG_ENTRY(parent_list_lookup)
+#define _xdg_mime_parent_list_dump             XDG_ENTRY(parent_list_dump)
+#endif
+
+void          _xdg_mime_parent_read_from_file (XdgParentList *list,
+                                              const char    *file_name);
+XdgParentList *_xdg_mime_parent_list_new       (void);
+void           _xdg_mime_parent_list_free      (XdgParentList *list);
+const char   **_xdg_mime_parent_list_lookup    (XdgParentList *list,
+                                               const char    *mime);
+void           _xdg_mime_parent_list_dump      (XdgParentList *list);
+
+#endif /* __XDG_MIME_PARENT_H__ */
index 9d5e486..38dc19d 100644 (file)
@@ -1,3 +1,8 @@
+2007-11-26  Alexander Larsson  <alexl@redhat.com>
+
+        * POTFILES.in:
+       Added gio sources
+
 2007-11-24  Claude Paroz  <claude@2xlibre.net>
 
        * fr.po: Updated French translation.
index 66deda6..f732b2f 100644 (file)
@@ -14,3 +14,79 @@ glib/gspawn.c
 glib/gutf8.c
 glib/goption.c
 glib/gkeyfile.c
+gio/gappinfo.c
+gio/gasyncresult.c
+gio/gbufferedinputstream.c
+gio/gbufferedoutputstream.c
+gio/gcancellable.c
+gio/gcontenttype.c
+gio/gdatainputstream.c
+gio/gdesktopappinfo.c
+gio/gdirectorymonitor.c
+gio/gdrive.c
+gio/gfile.c
+gio/gfileenumerator.c
+gio/gfileinfo.c
+gio/gfileinputstream.c
+gio/gfilemonitor.c
+gio/gfilenamecompleter.c
+gio/gfileoutputstream.c
+gio/gfilterinputstream.c
+gio/gfilteroutputstream.c
+gio/gicon.c
+gio/ginputstream.c
+gio/gloadableicon.c
+gio/glocalfile.c
+gio/glocalfileinfo.c
+gio/glocalfileinputstream.c
+gio/glocalfileoutputstream.c
+gio/gmemoryoutputstream.c
+gio/gmountoperation.c
+gio/goutputstream.c
+gio/gseekable.c
+gio/gsimpleasyncresult.c
+gio/gsocketinputstream.c
+gio/gsocketoutputstream.c
+gio/gunixdrive.c
+gio/gunixvolume.c
+gio/gvolume.c
+gio/gvolumemonitor.c
+gio/gwin32appinfo.c
+gio/gappinfo.c
+gio/gasyncresult.c
+gio/gbufferedinputstream.c
+gio/gbufferedoutputstream.c
+gio/gcancellable.c
+gio/gcontenttype.c
+gio/gdatainputstream.c
+gio/gdesktopappinfo.c
+gio/gdirectorymonitor.c
+gio/gdrive.c
+gio/gfile.c
+gio/gfileenumerator.c
+gio/gfileinfo.c
+gio/gfileinputstream.c
+gio/gfilemonitor.c
+gio/gfilenamecompleter.c
+gio/gfileoutputstream.c
+gio/gfilterinputstream.c
+gio/gfilteroutputstream.c
+gio/gicon.c
+gio/ginputstream.c
+gio/gloadableicon.c
+gio/glocalfile.c
+gio/glocalfileinfo.c
+gio/glocalfileinputstream.c
+gio/glocalfileoutputstream.c
+gio/gmemoryoutputstream.c
+gio/gmountoperation.c
+gio/goutputstream.c
+gio/gseekable.c
+gio/gsimpleasyncresult.c
+gio/gsocketinputstream.c
+gio/gsocketoutputstream.c
+gio/gunixdrive.c
+gio/gunixvolume.c
+gio/gvolume.c
+gio/gvolumemonitor.c
+gio/gwin32appinfo.c