[libgsystem/wip/filetreewalk: 2/2] wip: Recursive directory enumeration API



commit cfd4bfc8a9a00d2ebf482b0ca6aa0d6f49b8ffa4
Author: Colin Walters <walters verbum org>
Date:   Sun Apr 6 15:27:32 2014 -0400

    wip: Recursive directory enumeration API

 Makefile-libgsystem.am       |    7 +
 Makefile.am                  |    3 +
 Makefile.glib                |  302 ++++++++++++++++++++++++++++++++++++++++++
 configure.ac                 |    1 +
 src/gsystem-file-tree-walk.c |  219 ++++++++++++++++++++++++++++++
 src/gsystem-file-tree-walk.h |   62 +++++++++
 src/libgsystem.h             |    1 +
 7 files changed, 595 insertions(+), 0 deletions(-)
---
diff --git a/Makefile-libgsystem.am b/Makefile-libgsystem.am
index b75d517..bffacf0 100644
--- a/Makefile-libgsystem.am
+++ b/Makefile-libgsystem.am
@@ -22,6 +22,7 @@ libgsystemheader_HEADERS = \
        src/gsystem-local-alloc.h \
        src/gsystem-console.h \
        src/gsystem-file-utils.h \
+       src/gsystem-file-tree-walk.h \
        src/gsystem-glib-compat.h \
        src/gsystem-shutil.h \
        src/gsystem-log.h \
@@ -34,6 +35,7 @@ libgsystem_la_SOURCES = \
        src/gsystem-local-alloc.c \
        src/gsystem-console.c \
        src/gsystem-file-utils.c \
+       src/gsystem-file-tree-walk.c \
        src/gsystem-shutil.c \
        src/gsystem-log.c \
        src/gsystem-subprocess-context-private.h \
@@ -60,3 +62,8 @@ gir_DATA += GSystem-1.0.gir
 typelib_DATA += GSystem-1.0.typelib
 CLEANFILES += $(gir_DATA) $(typelib_DATA)
 endif
+
+GLIB_GENERATED += gsystem-enum-types.h gsystem-enum-types.c
+gsystem_enum_types_sources = $(libgsystemheader_HEADERS) $(libgsystem_la_SOURCES)
+gsystem_enum_types_MKENUMS_H_FLAGS = --identifier-prefix GS --symbol-prefix gs
+gsystem_enum_types_MKENUMS_C_FLAGS = --identifier-prefix GS --symbol-prefix gs
diff --git a/Makefile.am b/Makefile.am
index 472fbc1..171e408 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,6 +16,9 @@
 # Boston, MA 02111-1307, USA.
 
 include Makefile-decls.am
+include Makefile.glib
+GLIB_GENERATED =
+BUILT_SOURCES += $(GLIB_GENERATED)
 
 ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS}
 AM_CPPFLAGS += -DDATADIR='"$(datadir)"' -DLIBEXECDIR='"$(libexecdir)"' \
diff --git a/Makefile.glib b/Makefile.glib
new file mode 100644
index 0000000..dadbbdf
--- /dev/null
+++ b/Makefile.glib
@@ -0,0 +1,302 @@
+# -*- Mode: makefile -*-
+#
+# To use:
+#
+# In configure.ac:
+#   add -Wno-portability to AM_INIT_AUTOMAKE
+#   add GLIB_CONFIG([min-version[, required-modules]])
+#   (remove AM_PATH_GLIB_2_0 and GLIB_GSETTINGS)
+#
+# Add to Makefile.am where your library/program is built:
+#   include $(GLIB_MAKEFILE)
+#
+#   BUILT_SOURCES = $(GLIB_GENERATED)
+#
+# Add *.stamp to .gitignore
+#
+# Add a GLIB_GENERATED variable with the files you want to generate,
+# as described below. (The examples below use filenames with hyphens,
+# eg foo-marshal.h, but you can omit the hyphens if that matches your
+# file naming scheme better.)
+#
+# You do not need to modify CLEANFILES or EXTRA_DIST for any of these
+# macros.
+
+
+# glib-genmarshal
+#
+# To generate signal marshallers, add files with names ending in
+# "marshal.h" and "marshal.c" to GLIB_GENERATED:
+#
+#    GLIB_GENERATED += foo-marshal.h foo-marshal.c
+#    foo_marshal_sources = aaa.c bbb.c ccc.c ddd.c
+#
+# Makefile.glib will then generate a foo-marshal.list file containing
+# all _foo_marshal_* functions referenced by $(foo_marshal_sources),
+# and will rebuild foo-marshal.c/foo-marshal.h whenever the list
+# changes.
+#
+# For your convenience, any .h files or $(GLIB_GENERATED) files in
+# $(foo_marshal_sources) will be ignored. This means you can usually just
+# set foo_marshal_sources to the value of your library/program's
+# _SOURCES variable, even if that variable contains foo-marshal.c.
+#
+# You can set GLIB_GENMARSHAL_H_FLAGS and GLIB_GENMARSHAL_C_FLAGS (or
+# an appropriate file-specific variable, eg
+# foo_marshal_GENMARSHAL_H_FLAGS) to set/override glib-genmarshal
+# options.
+
+
+# glib-mkenums
+#
+# To generate enum type registrations, add files with names ending
+# in "-enum-types.[ch]" or "enumtypes.[ch]" to GLIB_GENERATED:
+#
+#    GLIB_GENERATED += foo-enum-types.h foo-enum-types.c
+#    foo_enum_types_sources = aaa.h bbb.h ccc.h ddd.h
+#
+# Makefile.glib will create a list all of the enum/flags types
+# declared in $(foo_enum_type_sources), and will rebuild
+# foo-enum-types.c/foo-enum-types.h whenever that list changes. (No
+# template files are required.)
+#
+# For your convenience, any .c files or $(GLIB_GENERATED) files in
+# $(foo_enum_types_sources) will be ignored. This means you can
+# usually set foo_enum_types_sources to the value of your
+# library/program's _HEADERS and/or _SOURCES variables, even if that
+# contains foo-enum-types.h.
+#
+# You can set GLIB_MKENUMS_H_FLAGS and GLIB_MKENUMS_C_FLAGS (or an
+# appropriate file-specific variable, eg
+# foo_enum_types_MKENUMS_H_FLAGS) to set/override glib-mkenums
+# options. In particular, you can do:
+#
+#     GLIB_MKENUMS_C_FLAGS = --fhead "\#define FOO_I_KNOW_THIS_IS_UNSTABLE"
+#
+# (The backslash is necessary to keep make from thinking the "#" is
+# the start of a comment.)
+
+
+# glib-compile-schemas
+#
+# Any foo.gschemas.xml files listed in gsettingsschema_DATA will be
+# validated before installation, and (if --disable-schemas-compile was
+# not passed) compiled after installation.
+#
+# To build an enums file, add it to GLIB_GENERATED (in addition to
+# gsettingsschema_DATA):
+#
+#     GLIB_GENERATED += org.gnome.foo.enums.xml
+#     org_gnome_foo_enums_xml_sources = aaa.h bbb.h ccc.h ddd.h
+#
+# All enums files will be built before any schema files are validated.
+
+
+########
+
+# Notes on Makefile.glib hacking:
+#
+#   - The exact rules that automake generates for a Makefile vary
+#     depending on what sorts of things were done in the Makefile.am,
+#     so we have to be careful with what rules we assume are there.
+#     In particular, (a) the glue to handle BUILT_SOURCES and the
+#     various hooks won't be output unless those things were
+#     referenced in the Makefile.am, and (b) a Makefile.am with
+#     SUBDIRS will get different rules than one without.
+#
+#   - Build rules should always refer to their dependencies via $^,
+#     not by reusing a variable that is listed in the rule's
+#     dependencies. This is needed to make srcdir!=builddir builds
+#     work. You can use $(filter)/$(filter-out) if $^ has things
+#     you don't want in it.
+#
+#   - When using a filename as something other than a filename,
+#     consider whether you need to wrap it in $(notdir) to get the
+#     right result when that file is being pulled out of a
+#     subdirectory.
+#
+#   - All private variables should be prefixed with _glib or _GLIB
+#
+#   - "make -qp > makefile.out" will give you a copy of the
+#     Makefile after all macros are expanded.
+#
+# The genmarshal code is commented; the mkenums and schema code is
+# generally similar.
+
+_GLIB_CLEANFILES =
+_GLIB_DISTCLEANFILES =
+
+_GLIB_V_GEN = $(_glib_v_gen_$(V))
+_glib_v_gen_ = $(_glib_v_gen_$(AM_DEFAULT_VERBOSITY))
+_glib_v_gen_0 = @echo "  GEN     " $(subst .stamp,,$@);
+
+
+### glib-genmarshal
+
+# _GLIB_MARSHAL_GENERATED contains the basenames (eg, "foo-marshal")
+# of all the marshal-related files to be generated.
+_GLIB_MARSHAL_GENERATED = $(subst .h,,$(filter %marshal.h,$(GLIB_GENERATED)))
+
+# These are used as macros (with the value of $(1) inherited from the "caller")
+#   _glib_marshal_prefix("foo-marshal") = "foo" (used in the C marshal names)
+#   _glib_marshal_sources_var("foo-marshal") = "foo_marshal_sources"
+#   _glib_marshal_sources = the filtered value of $(foo_marshal_sources)
+_glib_marshal_prefix = $(subst marshal,,$(subst _marshal,,$(subst -,_,$(notdir $(1)))))_marshal
+_glib_marshal_sources_var = $(subst -,_,$(notdir $(1)))_sources
+_glib_marshal_sources = $(filter-out %.h,$(filter-out $(GLIB_GENERATED),$($(_glib_marshal_sources_var))))
+
+# This is a multi-line macro (ending with the "endef" below) that
+# outputs a set of rules for a single .h/.c pair (whose basename is
+# $(1)). The initial $(if) line makes make error out if
+# foo_marshal_sources wasn't set. Note that single-$ variables are
+# expanded when the macro is called, and double-$ variables are
+# expanded when the rule is invoked.
+define _glib_make_genmarshal_rules
+$(if $(_glib_marshal_sources),,$(error Need to define $(_glib_marshal_sources_var) for $(1).[ch]))
+
+$(1).list.stamp: $(_glib_marshal_sources) Makefile
+       $$(_GLIB_V_GEN) LC_ALL=C sed -ne 's/.*_$(_glib_marshal_prefix)_\([_A-Z]*\).*/\1/p' $$(filter-out 
Makefile, $$^) | sort -u | sed -e 's/__/:/' -e 's/_/,/g' > $(1).list.tmp && \
+       (cmp -s $(1).list.tmp $(1).list || cp $(1).list.tmp $(1).list) && \
+       rm -f $(1).list.tmp && \
+       echo timestamp > $$@
+
+$(1).list: $(1).list.stamp
+       @true
+
+$(1).h: $(1).list
+       $$(_GLIB_V_GEN) $$(GLIB_GENMARSHAL) \
+               --prefix=_$(_glib_marshal_prefix) --header \
+               $$(GLIB_GENMARSHAL_H_FLAGS) \
+               $$($(_glib_marshal_prefix)_GENMARSHAL_H_FLAGS) \
+               $$< > $$  tmp && \
+       mv $$  tmp $$@
+
+$(1).c: $(1).list
+       $$(_GLIB_V_GEN) (echo "#include \"$$(subst .c,.h,$$(@F))\""; $$(GLIB_GENMARSHAL) \
+               --prefix=_$(_glib_marshal_prefix) --body \
+               $$(GLIB_GENMARSHAL_C_FLAGS) \
+               $$($(_glib_marshal_prefix)_GENMARSHAL_C_FLAGS) \
+               $$< ) > $$  tmp && \
+       mv $$  tmp $$@
+
+_GLIB_CLEANFILES += $(1).list.stamp $(1).list
+_GLIB_DISTCLEANFILES += $(1).h $(1).c
+endef
+
+# Run _glib_make_genmarshal_rules for each set of generated files
+$(foreach f,$(_GLIB_MARSHAL_GENERATED),$(eval $(call _glib_make_genmarshal_rules,$f)))
+
+
+### glib-mkenums
+
+_GLIB_ENUM_TYPES_GENERATED = $(subst .h,,$(filter %enum-types.h %enumtypes.h,$(GLIB_GENERATED)))
+
+_glib_enum_types_prefix = $(subst -,_,$(notdir $(1)))
+_glib_enum_types_guard = __$(shell LC_ALL=C echo $(_glib_enum_types_prefix) | tr 'a-z' 'A-Z')_H__
+_glib_enum_types_sources_var = $(_glib_enum_types_prefix)_sources
+_glib_enum_types_sources = $(filter-out $(GLIB_GENERATED),$($(_glib_enum_types_sources_var)))
+_glib_enum_types_h_sources = $(filter-out %-private.h,$(filter %.h,$(_glib_enum_types_sources)))
+
+define _glib_make_mkenums_rules
+$(if $(_glib_enum_types_sources),,$(error Need to define $(_glib_enum_types_sources_var) for $(1).[ch]))
+
+$(1).h.stamp: $(_glib_enum_types_h_sources) Makefile
+       $$(_GLIB_V_GEN) $$(GLIB_MKENUMS) \
+               --fhead "/* Generated by glib-mkenums. Do not edit */\n\n#ifndef 
$(_glib_enum_types_guard)\n#define $(_glib_enum_types_guard)\n\n" \
+               $$(GLIB_MKENUMS_H_FLAGS) \
+               $$($(_glib_enum_types_prefix)_MKENUMS_H_FLAGS) \
+               --fhead "#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+               --vhead "GType @enum_name _get_type (void) G_GNUC_CONST;\n#define @ENUMPREFIX 
_TYPE_@ENUMSHORT@ (@enum_name _get_type ())\n" \
+               --ftail "G_END_DECLS\n\n#endif /* $(_glib_enum_types_guard) */" \
+               $$(filter-out Makefile, $$^) > $(1).h.tmp && \
+       (cmp -s $(1).h.tmp $(1).h || cp $(1).h.tmp $(1).h) && \
+       rm -f $(1).h.tmp && \
+       echo timestamp > $$@
+
+$(1).h: $(1).h.stamp
+       @true
+
+$(1).c.stamp: $(_glib_enum_types_h_sources) Makefile
+       $$(_GLIB_V_GEN) $$(GLIB_MKENUMS) \
+               --fhead "/* Generated by glib-mkenums. Do not edit */\n\n#include \"$(notdir $(1)).h\"\n" \
+               $$(GLIB_MKENUMS_C_FLAGS) \
+               $$($(_glib_enum_types_prefix)_MKENUMS_C_FLAGS) \
+               --fhead "$$(foreach f,$$(filter-out Makefile,$$(^F)),\n#include \"$$(f)\")\n\n" \
+               --vhead "GType\n enum_name@_get_type (void)\n{\n  static volatile gsize 
g_define_type_id__volatile = 0;\n\n  if (g_once_init_enter (&g_define_type_id__volatile))\n    {\n      
static const G Type@Value values[] = {\n" \
+               --vprod "        { @VALUENAME@, \"@VALUENAME \", \"@valuenick \" },\n" \
+               --vtail "        { 0, NULL, NULL }\n      };\n      GType g_define_type_id =\n        g_ 
type@_register_static (g_intern_static_string (\"@EnumName \"), values);\n      g_once_init_leave 
(&g_define_type_id__volatile, g_define_type_id);\n    }\n\n  return g_define_type_id__volatile;\n}\n" \
+               $$(filter-out Makefile, $$^) > $(1).c.tmp && \
+       (cmp -s $(1).c.tmp $(1).c || cp $(1).c.tmp $(1).c) && \
+       rm -f $(1).c.tmp && \
+       echo timestamp > $$@
+
+$(1).c: $(1).c.stamp
+       @true
+
+_GLIB_CLEANFILES += $(1).h.stamp $(1).c.stamp
+_GLIB_DISTCLEANFILES += $(1).h $(1).c $(1).h.stamp $(1).c.stamp
+endef
+
+$(foreach f,$(_GLIB_ENUM_TYPES_GENERATED),$(eval $(call _glib_make_mkenums_rules,$f)))
+
+
+### glib-compile-schemas
+
+_GLIB_ENUMS_XML_GENERATED = $(filter %.enums.xml,$(GLIB_GENERATED))
+_GLIB_GSETTINGS_SCHEMA_FILES = $(filter %.gschema.xml,$(gsettingsschema_DATA))
+_GLIB_GSETTINGS_VALID_FILES = $(subst .xml,.valid,$(_GLIB_GSETTINGS_SCHEMA_FILES))
+
+_glib_enums_xml_prefix = $(subst .,_,$(notdir $(1)))
+_glib_enums_xml_sources_var = $(_glib_enums_xml_prefix)_sources
+_glib_enums_xml_sources = $(filter-out $(GLIB_GENERATED),$($(_glib_enums_xml_sources_var)))
+_glib_enums_xml_namespace = $(subst .enums.xml,,$(notdir $(1)))
+
+define _glib_make_enums_xml_rule
+$(if $(_glib_enums_xml_sources),,$(error Need to define $(_glib_enums_xml_sources_var) for $(1)))
+
+$(1): $(_glib_enums_xml_sources) Makefile
+       $$(_GLIB_V_GEN) $$(GLIB_MKENUMS) --comments '<!-- @comment@ -->' --fhead "<schemalist>" --vhead "  
<@type@ id='$(_glib_enums_xml_namespace)  EnumName@'>" --vprod "    <value nick='@valuenick@' 
value='@valuenum@'/>" --vtail "  </@type@>" --ftail "</schemalist>" $$(filter-out Makefile, $$^) > $$  tmp && 
mv $$  tmp $$@
+endef
+
+_GLIB_V_CHECK = $(_glib_v_check_$(V))
+_glib_v_check_ = $(_glib_v_check_$(AM_DEFAULT_VERBOSITY))
+_glib_v_check_0 = @echo "  CHECK   " $(subst .valid,.xml,$@);
+
+define _glib_make_schema_validate_rule
+$(subst .xml,.valid,$(1)): $(_GLIB_ENUMS_XML_GENERATED) $(1)
+       $$(_GLIB_V_CHECK) $$(GLIB_COMPILE_SCHEMAS) --strict --dry-run $$(addprefix --schema-file=,$$^) && 
touch $$@
+endef
+
+define _glib_make_schema_rules
+all-am: $(_GLIB_GSETTINGS_VALID_FILES)
+
+install-data-am: glib-install-schemas-hook
+
+glib-install-schemas-hook: install-gsettingsschemaDATA
+       @test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || (echo $(GLIB_COMPILE_SCHEMAS) 
$(gsettingsschemadir); $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir))
+
+uninstall-am: glib-uninstall-schemas-hook
+
+glib-uninstall-schemas-hook: uninstall-gsettingsschemaDATA
+       @test -n "$(GSETTINGS_DISABLE_SCHEMAS_COMPILE)$(DESTDIR)" || (echo $(GLIB_COMPILE_SCHEMAS) 
$(gsettingsschemadir); $(GLIB_COMPILE_SCHEMAS) $(gsettingsschemadir))
+
+.PHONY: glib-install-schemas-hook glib-uninstall-schemas-hook
+endef
+
+_GLIB_CLEANFILES += $(_GLIB_ENUMS_XML_GENERATED) $(_GLIB_GSETTINGS_VALID_FILES)
+
+$(foreach f,$(_GLIB_ENUMS_XML_GENERATED),$(eval $(call _glib_make_enums_xml_rule,$f)))
+$(foreach f,$(_GLIB_GSETTINGS_SCHEMA_FILES),$(eval $(call _glib_make_schema_validate_rule,$f)))
+$(if $(_GLIB_GSETTINGS_SCHEMA_FILES),$(eval $(_glib_make_schema_rules)))
+
+
+### Cleanup
+.PHONY: clean-glib distclean-glib
+
+clean-am: clean-glib
+clean-glib:
+       $(if $(strip $(_GLIB_CLEANFILES)),-rm -f $(_GLIB_CLEANFILES))
+
+distclean-am: distclean-glib
+distclean-glib:
+       $(if $(strip $(_GLIB_DISTCLEANFILES)),-rm -f $(_GLIB_DISTCLEANFILES))
diff --git a/configure.ac b/configure.ac
index bc608ad..bde4570 100644
--- a/configure.ac
+++ b/configure.ac
@@ -35,6 +35,7 @@ PKG_PROG_PKG_CONFIG
 
 GIO_DEPENDENCY="gio-unix-2.0 >= 2.34.0"
 PKG_CHECK_MODULES(BUILDDEP_GIO_UNIX, $GIO_DEPENDENCY)
+AM_PATH_GLIB_2_0
 
 GTK_DOC_CHECK([1.15], [--flavour no-tmpl])
 AC_PATH_PROG([XSLTPROC], [xsltproc])
diff --git a/src/gsystem-file-tree-walk.c b/src/gsystem-file-tree-walk.c
new file mode 100644
index 0000000..0d42479
--- /dev/null
+++ b/src/gsystem-file-tree-walk.c
@@ -0,0 +1,219 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014 Colin Walters <walters verbum org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/**
+ * SECTION:gsfiletreewalk
+ * @title: GSFileTreeWalk
+ * @short_description: Recurse over a directory tree
+ *
+ * While #GFileEnumerator provides an API to iterate over one
+ * directory, in many cases one wants to operate recursively.
+ * This API is designed to do that.
+ *
+ * In addition, a #GSFileTreeWalk contains Unix-native API such as
+ * file descriptor based enumeration.
+ */
+
+#include "config.h"
+
+#define _GSYSTEM_NO_LOCAL_ALLOC
+#include "libgsystem.h"
+#include "gsystem-file-tree-walk.h"
+#include "gsystem-enum-types.h"
+#include <glib-unix.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+
+/* Taken from systemd/src/shared/util.h */
+union dirent_storage {
+        struct dirent dent;
+        guint8 storage[offsetof(struct dirent, d_name) +
+                        ((NAME_MAX + 1 + sizeof(long)) & ~(sizeof(long) - 1))];
+};
+
+typedef GObjectClass GSFileTreeWalkClass;
+
+struct _GSFileTreeWalk
+{
+  GObject parent;
+
+  GSFileTreeWalkFlags flags;
+  int origin_dfd;
+  struct stat origin_stbuf;
+
+  guint owns_dfd : 1;
+};
+
+G_DEFINE_TYPE (GSFileTreeWalk, gs_file_tree_walk, G_TYPE_OBJECT);
+
+enum
+{
+  PROP_0,
+  PROP_FLAGS,
+  N_PROPS
+};
+
+static GParamSpec *gs_file_tree_walk_pspecs[N_PROPS];
+
+static void
+gs_file_tree_walk_init (GSFileTreeWalk  *self)
+{
+  self->origin_dfd = -1;
+}
+
+static void
+gs_file_tree_walk_finalize (GObject *object)
+{
+  GSFileTreeWalk *self = GS_FILE_TREE_WALK (object);
+
+  if (self->owns_dfd && self->origin_dfd != -1)
+    (void) close (self->origin_dfd);
+
+  if (G_OBJECT_CLASS (gs_file_tree_walk_class)->finalize != NULL)
+    G_OBJECT_CLASS (gs_file_tree_walk_class)->finalize (object);
+}
+
+static void
+gs_file_tree_walk_class_init (GSFileTreeWalkClass *class)
+{
+  /**
+   * GSFileTreeWalk:flags:
+   */
+  gs_file_tree_walk_pspecs[PROP_FLAGS] = g_param_spec_flags ("flags", "", "",
+                                                             GS_TYPE_FILE_TREE_WALK_FLAGS,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
+                                                             G_PARAM_STATIC_STRINGS);
+
+  g_object_class_install_properties (gobject_class, N_PROPS, gs_file_tree_walk_pspecs);
+}
+
+static void
+gs_file_tree_walk_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
+{
+  GSFileTreeWalk *self = GS_FILE_TREE_WALK (object);
+
+  switch (prop_id)
+    {
+    case PROP_FLAGS:
+      self->flags = g_value_get_flags (value);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+static void
+gs_file_tree_walk_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  GSFileTreeWalk *self = GS_FILE_TREE_WALK (object);
+
+  switch (prop_id)
+    {
+    case PROP_FLAGS:
+      g_value_set_flags (value, self->flags);
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+}
+
+/**
+ * gs_file_tree_walk_open:
+ * @path: Directory to enumerate
+ * @flags: Flags controlling enumeration
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Returns: (transfer full): A new directory enumerator
+ */
+GSFileTreeWalk *
+gs_file_tree_walk_open (GFile                   *path,
+                        GSFileTreeWalkFlags      flags,
+                        GCancellable            *cancellable,
+                        GError                 **error)
+{
+  gs_unref_object GSFileTreeWalk *ftw = g_object_new (GS_TYPE_FILE_TREE_WALK, "flags", flags, NULL);
+
+  ftw->owns_dfd = TRUE;
+  
+  if (!gs_file_open_dir_fd (path, &ftw->origin_dfd,
+                            cancellable, error))
+    return NULL;
+
+  return g_object_ref (ftw);
+}
+
+/**
+ * gs_file_tree_walk_open_at:
+ * @dfd: Directory file descriptor
+ * @flags: Flags controlling enumeration
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Returns: (transfer full): A new directory enumerator
+ */
+GSFileTreeWalk *
+gs_file_tree_walk_open_at (int                   dfd,
+                           GSFileTreeWalkFlags   flags,
+                           GCancellable         *cancellable,
+                           GError              **error)
+{
+  gs_unref_object GSFileTreeWalk *ftw = g_object_new (GS_TYPE_FILE_TREE_WALK, "flags", flags, NULL);
+
+  ftw->origin_dfd = dfd;
+  if (fstat (ftw->origin_dfd, &ftw->origin_stbuf) != 0)
+    {
+      int errsv = errno;
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "fstat: %s", g_strerror (errsv));
+      return NULL;
+    }
+
+  return g_object_ref (ftw);
+}
+
+/**
+ * gs_file_tree_walk_next:
+ * @self: Self
+ * @out_file_type: (out): File type, may be %G_FILE_TYPE_UNKNOWN
+ *
+ * Returns: %TRUE if there are more files to traverse
+ */
+gboolean
+gs_file_tree_walk_next (GSFileTreeWalk        *self,
+                        GFileType             *out_file_type)
+{
+}
+
+int gs_file_tree_walk_get_dirfd (GSFileTreeWalk   *self);
+
+const char *gs_file_tree_walk_get_name (GSFileTreeWalk   *self);
+
+char *gs_file_tree_walk_get_relpath (GSFileTreeWalk   *self);
diff --git a/src/gsystem-file-tree-walk.h b/src/gsystem-file-tree-walk.h
new file mode 100644
index 0000000..582ff7f
--- /dev/null
+++ b/src/gsystem-file-tree-walk.h
@@ -0,0 +1,62 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 2014 Colin Walters <walters verbum org>.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * 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 __GSYSTEM_FILE_TREEWALK_H__
+#define __GSYSTEM_FILE_TREEWALK_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GS_TYPE_FILE_TREE_WALK         (gs_file_tree_walk_get_type ())
+#define GS_FILE_TREE_WALK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GS_TYPE_FILE_TREE_WALK, 
GSFileTreeWalk))
+#define GS_IS_FILE_TREE_WALK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GS_TYPE_FILE_TREE_WALK))
+
+typedef struct _GSFileTreeWalk GSFileTreeWalk;
+
+GType gs_file_tree_walk_get_type (void) G_GNUC_CONST;
+
+typedef enum {
+  GS_FILE_TREE_WALK_FLAG_DEPTH = 1 << 0,
+  GS_FILE_TREE_WALK_FLAG_NOXDEV = 1 << 1
+} GSFileTreeWalkFlags;
+
+GSFileTreeWalk *gs_file_tree_walk_open (GFile                   *path,
+                                        GSFileTreeWalkFlags      flags,
+                                        GCancellable            *cancellable,
+                                        GError                 **error);
+
+GSFileTreeWalk *gs_file_tree_walk_open_at (int                   dfd,
+                                           GSFileTreeWalkFlags   flags,
+                                           GCancellable         *cancellable,
+                                           GError              **error);
+
+gboolean gs_file_tree_walk_next (GSFileTreeWalk        *self,
+                                 GFileType             *out_file_type);
+
+int gs_file_tree_walk_get_dirfd (GSFileTreeWalk   *self);
+
+const char *gs_file_tree_walk_get_name (GSFileTreeWalk   *self);
+
+char *gs_file_tree_walk_get_relpath (GSFileTreeWalk   *self);
+
+G_END_DECLS
+
+#endif
diff --git a/src/libgsystem.h b/src/libgsystem.h
index 60884b6..59df669 100644
--- a/src/libgsystem.h
+++ b/src/libgsystem.h
@@ -34,6 +34,7 @@ G_BEGIN_DECLS
   } G_STMT_END;
 
 #include <gsystem-console.h>
+#include <gsystem-enum-types.h>
 #include <gsystem-file-utils.h>
 #include <gsystem-shutil.h>
 #if GLIB_CHECK_VERSION(2,34,0)


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]