summaryrefslogtreecommitdiff
path: root/gio
diff options
context:
space:
mode:
authorAlexander Larsson <alexl@redhat.com>2007-11-26 16:13:05 +0000
committerAlexander Larsson <alexl@src.gnome.org>2007-11-26 16:13:05 +0000
commit3781343738de4abddf56982325a77bd70a98cd26 (patch)
tree7edf4bfd446dc1465fdb8595641bf5307955940a /gio
parent8bdbcb92135c8831d045874d5913a13fe30c3af4 (diff)
gio/ docs/reference/gio Merged gio-standalone into glib.
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
Diffstat (limited to 'gio')
-rw-r--r--gio/Makefile.am225
-rw-r--r--gio/fam/Makefile.am33
-rw-r--r--gio/fam/fam-helper.c245
-rw-r--r--gio/fam/fam-helper.h37
-rw-r--r--gio/fam/fam-module.c41
-rw-r--r--gio/fam/gfamdirectorymonitor.c152
-rw-r--r--gio/fam/gfamdirectorymonitor.h55
-rw-r--r--gio/fam/gfamfilemonitor.c150
-rw-r--r--gio/fam/gfamfilemonitor.h55
-rw-r--r--gio/gappinfo.c535
-rw-r--r--gio/gappinfo.h206
-rw-r--r--gio/gasynchelper.c163
-rw-r--r--gio/gasynchelper.h53
-rw-r--r--gio/gasyncresult.c107
-rw-r--r--gio/gasyncresult.h59
-rw-r--r--gio/gbufferedinputstream.c1230
-rw-r--r--gio/gbufferedinputstream.h110
-rw-r--r--gio/gbufferedoutputstream.c734
-rw-r--r--gio/gbufferedoutputstream.h76
-rw-r--r--gio/gcancellable.c322
-rw-r--r--gio/gcancellable.h74
-rw-r--r--gio/gcontenttype.c861
-rw-r--r--gio/gcontenttype.h50
-rw-r--r--gio/gcontenttypeprivate.h36
-rw-r--r--gio/gdatainputstream.c718
-rw-r--r--gio/gdatainputstream.h117
-rw-r--r--gio/gdataoutputstream.c441
-rw-r--r--gio/gdataoutputstream.h108
-rw-r--r--gio/gdesktopappinfo.c2185
-rw-r--r--gio/gdesktopappinfo.h54
-rw-r--r--gio/gdirectorymonitor.c472
-rw-r--r--gio/gdirectorymonitor.h86
-rw-r--r--gio/gdrive.c348
-rw-r--r--gio/gdrive.h99
-rw-r--r--gio/gdriveprivate.h32
-rw-r--r--gio/gdummyfile.c752
-rw-r--r--gio/gdummyfile.h51
-rw-r--r--gio/gfile.c4345
-rw-r--r--gio/gfile.h676
-rw-r--r--gio/gfileattribute.c704
-rw-r--r--gio/gfileattribute.h132
-rw-r--r--gio/gfileenumerator.c617
-rw-r--r--gio/gfileenumerator.h130
-rw-r--r--gio/gfileicon.c257
-rw-r--r--gio/gfileicon.h49
-rw-r--r--gio/gfileinfo.c1924
-rw-r--r--gio/gfileinfo.h280
-rw-r--r--gio/gfileinputstream.c481
-rw-r--r--gio/gfileinputstream.h110
-rw-r--r--gio/gfilemonitor.c380
-rw-r--r--gio/gfilemonitor.h97
-rw-r--r--gio/gfilenamecompleter.c489
-rw-r--r--gio/gfilenamecompleter.h66
-rw-r--r--gio/gfileoutputstream.c613
-rw-r--r--gio/gfileoutputstream.h121
-rw-r--r--gio/gfilterinputstream.c391
-rw-r--r--gio/gfilterinputstream.h65
-rw-r--r--gio/gfilteroutputstream.c366
-rw-r--r--gio/gfilteroutputstream.h65
-rw-r--r--gio/gicon.c118
-rw-r--r--gio/gicon.h58
-rw-r--r--gio/ginputstream.c1184
-rw-r--r--gio/ginputstream.h165
-rw-r--r--gio/gio-marshal.list4
-rw-r--r--gio/gioerror.c155
-rw-r--r--gio/gioerror.h78
-rw-r--r--gio/giomodule.c237
-rw-r--r--gio/giomodule.h52
-rw-r--r--gio/gioscheduler.c372
-rw-r--r--gio/gioscheduler.h55
-rw-r--r--gio/gloadableicon.c260
-rw-r--r--gio/gloadableicon.h83
-rw-r--r--gio/glocaldirectorymonitor.c290
-rw-r--r--gio/glocaldirectorymonitor.h65
-rw-r--r--gio/glocalfile.c1826
-rw-r--r--gio/glocalfile.h51
-rw-r--r--gio/glocalfileenumerator.c228
-rw-r--r--gio/glocalfileenumerator.h60
-rw-r--r--gio/glocalfileinfo.c2216
-rw-r--r--gio/glocalfileinfo.h68
-rw-r--r--gio/glocalfileinputstream.c319
-rw-r--r--gio/glocalfileinputstream.h60
-rw-r--r--gio/glocalfilemonitor.c211
-rw-r--r--gio/glocalfilemonitor.h59
-rw-r--r--gio/glocalfileoutputstream.c910
-rw-r--r--gio/glocalfileoutputstream.h73
-rw-r--r--gio/glocalvfs.c191
-rw-r--r--gio/glocalvfs.h47
-rw-r--r--gio/gmemoryinputstream.c461
-rw-r--r--gio/gmemoryinputstream.h73
-rw-r--r--gio/gmemoryoutputstream.c678
-rw-r--r--gio/gmemoryoutputstream.h73
-rw-r--r--gio/gmountoperation.c348
-rw-r--r--gio/gmountoperation.h126
-rw-r--r--gio/gnativevolumemonitor.c31
-rw-r--r--gio/gnativevolumemonitor.h35
-rw-r--r--gio/goutputstream.c1320
-rw-r--r--gio/goutputstream.h202
-rw-r--r--gio/gpollfilemonitor.c226
-rw-r--r--gio/gpollfilemonitor.h50
-rw-r--r--gio/gseekable.c168
-rw-r--r--gio/gseekable.h81
-rw-r--r--gio/gsimpleasyncresult.c586
-rw-r--r--gio/gsimpleasyncresult.h115
-rw-r--r--gio/gsocketinputstream.c469
-rw-r--r--gio/gsocketinputstream.h68
-rw-r--r--gio/gsocketoutputstream.c426
-rw-r--r--gio/gsocketoutputstream.h68
-rw-r--r--gio/gthemedicon.c172
-rw-r--r--gio/gthemedicon.h49
-rw-r--r--gio/gunionvolumemonitor.c392
-rw-r--r--gio/gunionvolumemonitor.h54
-rw-r--r--gio/gunixdrive.c321
-rw-r--r--gio/gunixdrive.h59
-rw-r--r--gio/gunixmounts.c1515
-rw-r--r--gio/gunixmounts.h92
-rw-r--r--gio/gunixvolume.c332
-rw-r--r--gio/gunixvolume.h57
-rw-r--r--gio/gunixvolumemonitor.c382
-rw-r--r--gio/gunixvolumemonitor.h57
-rw-r--r--gio/gurifuncs.c276
-rw-r--r--gio/gurifuncs.h55
-rw-r--r--gio/gvfs.c258
-rw-r--r--gio/gvfs.h98
-rw-r--r--gio/gvolume.c326
-rw-r--r--gio/gvolume.h97
-rw-r--r--gio/gvolumemonitor.c143
-rw-r--r--gio/gvolumemonitor.h88
-rw-r--r--gio/gvolumeprivate.h34
-rw-r--r--gio/gwin32appinfo.c672
-rw-r--r--gio/gwin32appinfo.h50
-rw-r--r--gio/inotify/Makefile.am34
-rw-r--r--gio/inotify/ginotifydirectorymonitor.c144
-rw-r--r--gio/inotify/ginotifydirectorymonitor.h54
-rw-r--r--gio/inotify/ginotifyfilemonitor.c162
-rw-r--r--gio/inotify/ginotifyfilemonitor.h54
-rw-r--r--gio/inotify/inotify-diag.c74
-rw-r--r--gio/inotify/inotify-diag.h29
-rw-r--r--gio/inotify/inotify-helper.c264
-rw-r--r--gio/inotify/inotify-helper.h33
-rw-r--r--gio/inotify/inotify-kernel.c676
-rw-r--r--gio/inotify/inotify-kernel.h55
-rw-r--r--gio/inotify/inotify-missing.c167
-rw-r--r--gio/inotify/inotify-missing.h35
-rw-r--r--gio/inotify/inotify-path.c417
-rw-r--r--gio/inotify/inotify-path.h33
-rw-r--r--gio/inotify/inotify-sub.c68
-rw-r--r--gio/inotify/inotify-sub.h38
-rw-r--r--gio/inotify/local_inotify.h113
-rw-r--r--gio/inotify/local_inotify_syscalls.h85
-rw-r--r--gio/xdgmime/.gitignore1
-rw-r--r--gio/xdgmime/Makefile.am24
-rw-r--r--gio/xdgmime/test-mime.c142
-rw-r--r--gio/xdgmime/xdgmime.c864
-rw-r--r--gio/xdgmime/xdgmime.h120
-rw-r--r--gio/xdgmime/xdgmimealias.c184
-rw-r--r--gio/xdgmime/xdgmimealias.h51
-rw-r--r--gio/xdgmime/xdgmimecache.c909
-rw-r--r--gio/xdgmime/xdgmimecache.h76
-rw-r--r--gio/xdgmime/xdgmimeglob.c547
-rw-r--r--gio/xdgmime/xdgmimeglob.h67
-rw-r--r--gio/xdgmime/xdgmimeint.c154
-rw-r--r--gio/xdgmime/xdgmimeint.h73
-rw-r--r--gio/xdgmime/xdgmimemagic.c813
-rw-r--r--gio/xdgmime/xdgmimemagic.h57
-rw-r--r--gio/xdgmime/xdgmimeparent.c219
-rw-r--r--gio/xdgmime/xdgmimeparent.h51
167 files changed, 50995 insertions, 0 deletions
diff --git a/gio/Makefile.am b/gio/Makefile.am
new file mode 100644
index 000000000..31b3e5c7b
--- /dev/null
+++ b/gio/Makefile.am
@@ -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
index 000000000..0e8b79969
--- /dev/null
+++ b/gio/fam/Makefile.am
@@ -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
index 000000000..40c2c6ac3
--- /dev/null
+++ b/gio/fam/fam-helper.c
@@ -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
index 000000000..e8acbfeff
--- /dev/null
+++ b/gio/fam/fam-helper.h
@@ -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
index 000000000..d7b9cfcae
--- /dev/null
+++ b/gio/fam/fam-module.c
@@ -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
index 000000000..89298301b
--- /dev/null
+++ b/gio/fam/gfamdirectorymonitor.c
@@ -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
index 000000000..eefda883d
--- /dev/null
+++ b/gio/fam/gfamdirectorymonitor.h
@@ -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
index 000000000..be3631806
--- /dev/null
+++ b/gio/fam/gfamfilemonitor.c
@@ -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
index 000000000..559384eb4
--- /dev/null
+++ b/gio/fam/gfamfilemonitor.h
@@ -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
index 000000000..922d70072
--- /dev/null
+++ b/gio/gappinfo.c
@@ -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
index 000000000..0a88349d4
--- /dev/null
+++ b/gio/gappinfo.h
@@ -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
index 000000000..b6a9352c7
--- /dev/null
+++ b/gio/gasynchelper.c
@@ -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
index 000000000..0f57a4093
--- /dev/null
+++ b/gio/gasynchelper.h
@@ -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
index 000000000..840931976
--- /dev/null
+++ b/gio/gasyncresult.c
@@ -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
index 000000000..26f867e02
--- /dev/null
+++ b/gio/gasyncresult.h
@@ -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
index 000000000..18469a041
--- /dev/null
+++ b/gio/gbufferedinputstream.c
@@ -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
index 000000000..eab35b5b5
--- /dev/null
+++ b/gio/gbufferedinputstream.h
@@ -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
index 000000000..210ccfb0d
--- /dev/null
+++ b/gio/gbufferedoutputstream.c
@@ -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
index 000000000..54086448c
--- /dev/null
+++ b/gio/gbufferedoutputstream.h
@@ -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
index 000000000..c55737a32
--- /dev/null
+++ b/gio/gcancellable.c
@@ -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
index 000000000..a6229da4f
--- /dev/null
+++ b/gio/gcancellable.h
@@ -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
index 000000000..5952cacdb
--- /dev/null
+++ b/gio/gcontenttype.c
@@ -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
index 000000000..e8bbf5780
--- /dev/null
+++ b/gio/gcontenttype.h
@@ -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
index 000000000..a94d13895
--- /dev/null
+++ b/gio/gcontenttypeprivate.h
@@ -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
index 000000000..54a46784f
--- /dev/null
+++ b/gio/gdatainputstream.c
@@ -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
index 000000000..af58dbfbe
--- /dev/null
+++ b/gio/gdatainputstream.h
@@ -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
index 000000000..6393d7c82
--- /dev/null
+++ b/gio/gdataoutputstream.c
@@ -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
index 000000000..94bdfa5c2
--- /dev/null
+++ b/gio/gdataoutputstream.h
@@ -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
index 000000000..e8e77cf00
--- /dev/null
+++ b/gio/gdesktopappinfo.c
@@ -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
index 000000000..0f667dca3
--- /dev/null
+++ b/gio/gdesktopappinfo.h
@@ -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
index 000000000..eb50a958c
--- /dev/null
+++ b/gio/gdirectorymonitor.c
@@ -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
index 000000000..d4e357307
--- /dev/null
+++ b/gio/gdirectorymonitor.h
@@ -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
index 000000000..b953c2c88
--- /dev/null
+++ b/gio/gdrive.c
@@ -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
index 000000000..7a0413ea9
--- /dev/null
+++ b/gio/gdrive.h
@@ -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
index 000000000..bd53b2a20
--- /dev/null
+++ b/gio/gdriveprivate.h
@@ -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
index 000000000..40ac3b166
--- /dev/null
+++ b/gio/gdummyfile.c
@@ -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
index 000000000..43e45177f
--- /dev/null
+++ b/gio/gdummyfile.h
@@ -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
index 000000000..617422f17
--- /dev/null
+++ b/gio/gfile.c
@@ -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
index 000000000..ac3f02f14
--- /dev/null
+++ b/gio/gfile.h
@@ -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
index 000000000..512500b3c
--- /dev/null
+++ b/gio/gfileattribute.c
@@ -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
index 000000000..125a46d45
--- /dev/null
+++ b/gio/gfileattribute.h
@@ -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
index 000000000..300fff316
--- /dev/null
+++ b/gio/gfileenumerator.c
@@ -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
index 000000000..1e83f8856
--- /dev/null
+++ b/gio/gfileenumerator.h
@@ -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
index 000000000..6e46e7f95
--- /dev/null
+++ b/gio/gfileicon.c
@@ -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
index 000000000..1745e5fba
--- /dev/null
+++ b/gio/gfileicon.h
@@ -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
index 000000000..07c8f4918
--- /dev/null
+++ b/gio/gfileinfo.c
@@ -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
index 000000000..480d6a6de
--- /dev/null
+++ b/gio/gfileinfo.h
@@ -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
index 000000000..24c8a61fc
--- /dev/null
+++ b/gio/gfileinputstream.c
@@ -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
index 000000000..ecf876634
--- /dev/null
+++ b/gio/gfileinputstream.h
@@ -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
index 000000000..ba9fa561b
--- /dev/null
+++ b/gio/gfilemonitor.c
@@ -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
index 000000000..edb9f64be
--- /dev/null
+++ b/gio/gfilemonitor.h
@@ -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
index 000000000..c8d85d817
--- /dev/null
+++ b/gio/gfilenamecompleter.c
@@ -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
index 000000000..cf4cae198
--- /dev/null
+++ b/gio/gfilenamecompleter.h
@@ -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
index 000000000..40914d671
--- /dev/null
+++ b/gio/gfileoutputstream.c
@@ -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
index 000000000..6525754e1
--- /dev/null
+++ b/gio/gfileoutputstream.h
@@ -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
index 000000000..6d2f99a33
--- /dev/null
+++ b/gio/gfilterinputstream.c
@@ -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
index 000000000..331f9be68
--- /dev/null
+++ b/gio/gfilterinputstream.h
@@ -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
index 000000000..ca41be5f9
--- /dev/null
+++ b/gio/gfilteroutputstream.c
@@ -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
index 000000000..604f9283d
--- /dev/null
+++ b/gio/gfilteroutputstream.h
@@ -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
index 000000000..5913ac3ad
--- /dev/null
+++ b/gio/gicon.c
@@ -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
index 000000000..6c0dbc0de
--- /dev/null
+++ b/gio/gicon.h
@@ -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
index 000000000..127f5200e
--- /dev/null
+++ b/gio/ginputstream.c
@@ -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
index 000000000..7df266298
--- /dev/null
+++ b/gio/ginputstream.h
@@ -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
index 000000000..571666316
--- /dev/null
+++ b/gio/gio-marshal.list
@@ -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
index 000000000..9b1553d54
--- /dev/null
+++ b/gio/gioerror.c
@@ -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
index 000000000..c4606fcec
--- /dev/null
+++ b/gio/gioerror.h
@@ -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
index 000000000..ab7c29d14
--- /dev/null
+++ b/gio/giomodule.c
@@ -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
index 000000000..44ed052f1
--- /dev/null
+++ b/gio/giomodule.h
@@ -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
index 000000000..1c5f5485d
--- /dev/null
+++ b/gio/gioscheduler.c
@@ -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
index 000000000..73f2684e5
--- /dev/null
+++ b/gio/gioscheduler.h
@@ -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
index 000000000..569065e94
--- /dev/null
+++ b/gio/gloadableicon.c
@@ -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
index 000000000..ef203d709
--- /dev/null
+++ b/gio/gloadableicon.h
@@ -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
index 000000000..0cdcf8aa4
--- /dev/null
+++ b/gio/glocaldirectorymonitor.c
@@ -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
index 000000000..9b8f9ed4d
--- /dev/null
+++ b/gio/glocaldirectorymonitor.h
@@ -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
index 000000000..9d5b92606
--- /dev/null
+++ b/gio/glocalfile.c
@@ -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
index 000000000..62e22f648
--- /dev/null
+++ b/gio/glocalfile.h
@@ -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
index 000000000..8548fcfad
--- /dev/null
+++ b/gio/glocalfileenumerator.c
@@ -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
index 000000000..67b49ed69
--- /dev/null
+++ b/gio/glocalfileenumerator.h
@@ -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
index 000000000..5f6176be5
--- /dev/null
+++ b/gio/glocalfileinfo.c
@@ -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
index 000000000..888d5ba38
--- /dev/null
+++ b/gio/glocalfileinfo.h
@@ -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
index 000000000..3f90720c2
--- /dev/null
+++ b/gio/glocalfileinputstream.c
@@ -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
index 000000000..259ed8eb7
--- /dev/null
+++ b/gio/glocalfileinputstream.h
@@ -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
index 000000000..94ebf7b61
--- /dev/null
+++ b/gio/glocalfilemonitor.c
@@ -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
index 000000000..b11ce8575
--- /dev/null
+++ b/gio/glocalfilemonitor.h
@@ -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
index 000000000..c8b58c9da
--- /dev/null
+++ b/gio/glocalfileoutputstream.c
@@ -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
index 000000000..fc8237d85
--- /dev/null
+++ b/gio/glocalfileoutputstream.h
@@ -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
index 000000000..8a39f9866
--- /dev/null
+++ b/gio/glocalvfs.c
@@ -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
index 000000000..dee124c3e
--- /dev/null
+++ b/gio/glocalvfs.h
@@ -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
index 000000000..fea27a4d7
--- /dev/null
+++ b/gio/gmemoryinputstream.c
@@ -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
index 000000000..d62cb7fb2
--- /dev/null
+++ b/gio/gmemoryinputstream.h
@@ -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
index 000000000..966f34be0
--- /dev/null
+++ b/gio/gmemoryoutputstream.c
@@ -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
index 000000000..bcb3cc7de
--- /dev/null
+++ b/gio/gmemoryoutputstream.h
@@ -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
index 000000000..034854bd3
--- /dev/null
+++ b/gio/gmountoperation.c
@@ -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
index 000000000..3b730b5ff
--- /dev/null
+++ b/gio/gmountoperation.h
@@ -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
index 000000000..cc2d81a07
--- /dev/null
+++ b/gio/gnativevolumemonitor.c
@@ -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
index 000000000..c3ccca2ee
--- /dev/null
+++ b/gio/gnativevolumemonitor.h
@@ -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
index 000000000..514ea87ba
--- /dev/null
+++ b/gio/goutputstream.c
@@ -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
index 000000000..4bfb621e2
--- /dev/null
+++ b/gio/goutputstream.h
@@ -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
index 000000000..0b5a5e79d
--- /dev/null
+++ b/gio/gpollfilemonitor.c
@@ -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
index 000000000..2d4e2a37a
--- /dev/null
+++ b/gio/gpollfilemonitor.h
@@ -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
index 000000000..90f53da0a
--- /dev/null
+++ b/gio/gseekable.c
@@ -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
index 000000000..33fc619b8
--- /dev/null
+++ b/gio/gseekable.h
@@ -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
index 000000000..3c69f19d9
--- /dev/null
+++ b/gio/gsimpleasyncresult.c
@@ -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
index 000000000..4c4ecf8e6
--- /dev/null
+++ b/gio/gsimpleasyncresult.h
@@ -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
index 000000000..890e8e1f8
--- /dev/null
+++ b/gio/gsocketinputstream.c
@@ -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
index 000000000..31b79ce50
--- /dev/null
+++ b/gio/gsocketinputstream.h
@@ -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
index 000000000..54539f589
--- /dev/null
+++ b/gio/gsocketoutputstream.c
@@ -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
index 000000000..cff198654
--- /dev/null
+++ b/gio/gsocketoutputstream.h
@@ -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
index 000000000..e27b03374
--- /dev/null
+++ b/gio/gthemedicon.c
@@ -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
index 000000000..f560f1871
--- /dev/null
+++ b/gio/gthemedicon.h
@@ -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
index 000000000..bc46d4c64
--- /dev/null
+++ b/gio/gunionvolumemonitor.c
@@ -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
index 000000000..8cb3ecb77
--- /dev/null
+++ b/gio/gunionvolumemonitor.h
@@ -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
index 000000000..464399264
--- /dev/null
+++ b/gio/gunixdrive.c
@@ -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
index 000000000..869ed8451
--- /dev/null
+++ b/gio/gunixdrive.h
@@ -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
index 000000000..da23bd4c0
--- /dev/null
+++ b/gio/gunixmounts.c
@@ -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
index 000000000..7e9c03b11
--- /dev/null
+++ b/gio/gunixmounts.h
@@ -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
index 000000000..6ce1260c7
--- /dev/null
+++ b/gio/gunixvolume.c
@@ -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
index 000000000..b0cb40600
--- /dev/null
+++ b/gio/gunixvolume.h
@@ -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
index 000000000..2430e4bcc
--- /dev/null
+++ b/gio/gunixvolumemonitor.c
@@ -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
index 000000000..feb940943
--- /dev/null
+++ b/gio/gunixvolumemonitor.h
@@ -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
index 000000000..2e1072319
--- /dev/null
+++ b/gio/gurifuncs.c
@@ -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
index 000000000..cace2d90e
--- /dev/null
+++ b/gio/gurifuncs.h
@@ -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
index 000000000..4dc1f256b
--- /dev/null
+++ b/gio/gvfs.c
@@ -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
index 000000000..ebc82bded
--- /dev/null
+++ b/gio/gvfs.h
@@ -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
index 000000000..280f46c26
--- /dev/null
+++ b/gio/gvolume.c
@@ -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
index 000000000..38addbe19
--- /dev/null
+++ b/gio/gvolume.h
@@ -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
index 000000000..a1b7c9b82
--- /dev/null
+++ b/gio/gvolumemonitor.c
@@ -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
index 000000000..ef990a1c4
--- /dev/null
+++ b/gio/gvolumemonitor.h
@@ -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
index 000000000..ce959de82
--- /dev/null
+++ b/gio/gvolumeprivate.h
@@ -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
index 000000000..946b55943
--- /dev/null
+++ b/gio/gwin32appinfo.c
@@ -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
index 000000000..cfec62f1e
--- /dev/null
+++ b/gio/gwin32appinfo.h
@@ -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
index 000000000..191f27dd2
--- /dev/null
+++ b/gio/inotify/Makefile.am
@@ -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
index 000000000..bec36093c
--- /dev/null
+++ b/gio/inotify/ginotifydirectorymonitor.c
@@ -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
index 000000000..bc170983a
--- /dev/null
+++ b/gio/inotify/ginotifydirectorymonitor.h
@@ -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
index 000000000..4132116a0
--- /dev/null
+++ b/gio/inotify/ginotifyfilemonitor.c
@@ -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
index 000000000..ae877bc7b
--- /dev/null
+++ b/gio/inotify/ginotifyfilemonitor.h
@@ -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
index 000000000..937ebd702
--- /dev/null
+++ b/gio/inotify/inotify-diag.c
@@ -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
index 000000000..f818f1616
--- /dev/null
+++ b/gio/inotify/inotify-diag.h
@@ -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
index 000000000..82a68de2e
--- /dev/null
+++ b/gio/inotify/inotify-helper.c
@@ -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
index 000000000..1fd9701d4
--- /dev/null
+++ b/gio/inotify/inotify-helper.h
@@ -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
index 000000000..6735c455e
--- /dev/null
+++ b/gio/inotify/inotify-kernel.c
@@ -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
index 000000000..b406d71d5
--- /dev/null
+++ b/gio/inotify/inotify-kernel.h
@@ -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
index 000000000..96126b3c8
--- /dev/null
+++ b/gio/inotify/inotify-missing.c
@@ -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
index 000000000..b67b59525
--- /dev/null
+++ b/gio/inotify/inotify-missing.h
@@ -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
index 000000000..76fc06d79
--- /dev/null
+++ b/gio/inotify/inotify-path.c
@@ -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
index 000000000..c613b9f82
--- /dev/null
+++ b/gio/inotify/inotify-path.h
@@ -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
index 000000000..f67a56318
--- /dev/null
+++ b/gio/inotify/inotify-sub.c
@@ -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
index 000000000..36561e74c
--- /dev/null
+++ b/gio/inotify/inotify-sub.h
@@ -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
index 000000000..267c88b5f
--- /dev/null
+++ b/gio/inotify/local_inotify.h
@@ -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
index 000000000..1821accef
--- /dev/null
+++ b/gio/inotify/local_inotify_syscalls.h
@@ -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
index 000000000..56e694593
--- /dev/null
+++ b/gio/xdgmime/.gitignore
@@ -0,0 +1 @@
+test-mime
diff --git a/gio/xdgmime/Makefile.am b/gio/xdgmime/Makefile.am
new file mode 100644
index 000000000..41f16a7ea
--- /dev/null
+++ b/gio/xdgmime/Makefile.am
@@ -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
index 000000000..7cff59b49
--- /dev/null
+++ b/gio/xdgmime/test-mime.c
@@ -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
index 000000000..0e11b070a
--- /dev/null
+++ b/gio/xdgmime/xdgmime.c
@@ -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
index 000000000..b8fd2d50f
--- /dev/null
+++ b/gio/xdgmime/xdgmime.h
@@ -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
index 000000000..07d89eb32
--- /dev/null
+++ b/gio/xdgmime/xdgmimealias.c
@@ -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
index 000000000..d0aaed052
--- /dev/null
+++ b/gio/xdgmime/xdgmimealias.h
@@ -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
index 000000000..f17ddf245
--- /dev/null
+++ b/gio/xdgmime/xdgmimecache.c
@@ -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
index 000000000..1cd978fae
--- /dev/null
+++ b/gio/xdgmime/xdgmimecache.h
@@ -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
index 000000000..3aad6113c
--- /dev/null
+++ b/gio/xdgmime/xdgmimeglob.c
@@ -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
index 000000000..25a1f20e5
--- /dev/null
+++ b/gio/xdgmime/xdgmimeglob.h
@@ -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
index 000000000..4a0ac4cc3
--- /dev/null
+++ b/gio/xdgmime/xdgmimeint.c
@@ -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
index 000000000..288148719
--- /dev/null
+++ b/gio/xdgmime/xdgmimeint.h
@@ -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
index 000000000..a2320f584
--- /dev/null
+++ b/gio/xdgmime/xdgmimemagic.c
@@ -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
index 000000000..8f113051e
--- /dev/null
+++ b/gio/xdgmime/xdgmimemagic.h
@@ -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
index 000000000..511bbacbc
--- /dev/null
+++ b/gio/xdgmime/xdgmimeparent.c
@@ -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
index 000000000..257ea0497
--- /dev/null
+++ b/gio/xdgmime/xdgmimeparent.h
@@ -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__ */