GNOME Bugzilla – Bug 432651
Add a glib-ish xdg_user_dir_lookup
Last modified: 2009-06-22 21:47:21 UTC
xdg-user-dirs allows to use defaults for some known directories. See http://freedesktop.org/wiki/Software_2fxdg_2duser_2ddirs It would be nice if glib had a version of xdg_user_dir_lookup, so the applications don't need to cut'n'paste the code. It would also be useful if this code could monitor ~/.config/user-dirs.dirs and /etc/xdg/user-dirs.defaults so that applications are told straight away about possible location changes.
Humm, dupe of bug 328679?
g_user_data_dir() and g_user_config_dir() would be useful to implement this piece of code, but the purpose of this bug is to look up default user directories for a specific type of data, ie. xdg_user_dir_lookup ("VIDEO") would return ~/Movies
Is anyone working on this? If not, I'll give it a try
I haven't looked into implementing this either.
Remember that there are two common use cases for this: 1) quick one-time lookup when the user initiated an operations. This can basically read the config file at the time and rely on kernel caching to make it work mostly ok. 2) Tracking a specific directory during the display of something. Say nautilus tracking the desktop directory. It looks this up a *lot*, and would like this to be cached, but still it wants some form of updates when the setting changes. Ideally using file notification, but a cache that rereads the config file on access if the last read was more than N seconds ago might do.
We just added Win32 and Carbon versions of that functionality to GIMP. It's tested on the platforms and works. So if this goes into GLib, just take the code: http://svn.gnome.org/viewcvs/gimp/trunk/libgimpbase/gimpenv.c?view=markup (function gimp_user_directory())
Created attachment 88740 [details] [review] [PATCH] Retrieve special directories This is a preliminary patch adding the g_get_user_special_dir() function. This function returns the full path for "special" directories, depending on the platform, using a logical ID. On G_OS_WIN, SHGetSpecialFolderLocation() is used; on G_OS_UNIX, the user configuration file defined by the xdg-user-dirs specification is parsed and the path is extracted from it, resolving the $HOME variable if needed. --- glib/gutils.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ glib/gutils.h | 15 ++++ tests/testglib.c | 6 ++ 3 files changed, 241 insertions(+), 0 deletions(-)
Created attachment 88755 [details] [review] [PATCH] Add configure check for Carbon This patch adds a configure-time check for the Carbon framework and adds the LDFLAGS necessary. It also adds a conditional (OS_CARBON) for Makefile templates and a define (HAVE_CARBON) for source files including "config.h". --- configure.in | 18 +++++++++++++++++- 1 files changed, 17 insertions(+), 1 deletions(-)
Created attachment 88756 [details] [review] [PATCH] Retrieve special folders via logical id. This adds the g_get_user_special_dir() function. This function returns the full path for "special" directories, depending on the platform, using a logical ID. On G_OS_WIN, SHGetSpecialFolderLocation() is used; on G_OS_UNIX, the user configuration file defined by the xdg-user-dirs specification is parsed and the path is extracted from it, resolving the $HOME variable if needed; on OS X, Carbon is used to retrieve the special directories. Each path found is cached the first time it's retrieved; the cache expires after fifteen minutes, in case the user changed the location of the directory. --- glib/gutils.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ glib/gutils.h | 15 +++ 2 files changed, 314 insertions(+), 0 deletions(-)
Created attachment 88757 [details] [review] [PATCH] Test and document the special directories patch Add a test for the newly added g_get_user_special_dir() function; export the symbol; let gtk-doc pick up the inline documentation. --- docs/reference/glib/glib-sections.txt | 2 ++ docs/reference/glib/tmpl/main.sgml | 3 +++ docs/reference/glib/tmpl/misc_utils.sgml | 24 ++++++++++++++++++++++++ docs/reference/gobject/tmpl/gparamspec.sgml | 2 ++ glib/glib.symbols | 1 + tests/testglib.c | 6 ++++++ 6 files changed, 38 insertions(+), 0 deletions(-)
(In reply to comment #9) > Created an attachment (id=88756) [edit] > [PATCH] Retrieve special folders via logical id. <snip> > Each path found is cached the first time it's retrieved; the cache expires > after fifteen minutes, in case the user changed the location of the directory. Why not check if it's actually changed by either using inotify, or checking the mtime the next time it's accessed?
using inotify would add the dependency to glib *and* would only work on linux, while the xdg-user-dirs code is meant to work on every *nix, as far as I understand. checking the mtime might be better, in this case.
G_OS_UNIX is TRUE on OSX too, so the xdg code must only be built if (!G_OS_WIN32 && !HAVE_CARBON)
(In reply to comment #11) > Why not check if it's actually changed by either using inotify, or checking the > mtime the next time it's accessed? on second thought: given that we choose to cache the results, we need a way to expire the cache in the win32 and xdg-user-dirs cases, as both platforms can change the location of the directory associated to the logical id. it's not known if the carbon platform can do that too. so having a common expiration code path makes sense. if we decide not to cache the results, there is a penalty for the xdg-user-dirs platform, where we need to hit a file; if both carbon and win32 calls are not smart enough, it would also have a penalty on those platforms (hit the registry/whatever osx uses). so having an expiration time for the cache seems a good compromise, for the time being. (In reply to comment #13) > G_OS_UNIX is TRUE on OSX too, so the xdg code must only be built > if (!G_OS_WIN32 && !HAVE_CARBON) thanks, fixed in my trunk, will attach a patch (with the full license for the xdg_user_dir_lookup() function too).
Created attachment 88844 [details] [review] Retrieve special folders via logical id This adds the g_get_user_special_dir() function. This function returns the full path for "special" directories, depending on the platform, using a logical ID. On G_OS_WIN, SHGetSpecialFolderLocation() is used; on G_OS_UNIX, the user configuration file defined by the xdg-user-dirs specification is parsed and the path is extracted from it, resolving the $HOME variable if needed; on OS X, Carbon is used to retrieve the special directories. Each path found is cached the first time it's retrieved; the cache expires after fifteen minutes, in case the user changed the location of the directory. This iteration fixes some issues of g_get_user_special_dir(): * do not compile xdg_user_dir_lookup() on Carbon; * add MIT license notice above xdg_user_dir_lookup(); * load the entire file and split it in lines, instead of iterating over it; * resolve a bare "$HOME" to the user's home directory; * remove trailing slashes. configure.in | 18 + docs/reference/glib/glib-sections.txt | 2 docs/reference/glib/tmpl/misc_utils.sgml | 24 ++ glib/glib.symbols | 1 glib/gutils.c | 326 +++++++++++++++++++++++++++++++ glib/gutils.h | 15 + tests/testglib.c | 8 7 files changed, 393 insertions(+), 1 deletion(-)
For OS X, could you please adjust the configure tests to distinguish between (or at least clarify the flag-names for) the platform itself vs the way you expect to locate the special folders on that platform? As it stands, they are mixed: approximately "presence of Carbon.h, therefore use its special-dir lookups instead of xdg's". My concern is for OS X users who want a self-consistent X11 world and therefore would like glib2 to use xdg. Maybe configure.in not gutils.c should have the logic to decide what back-end to use for g_get_user_special_dir() and pass it in as the value of a SPECIAL_DIR_SUPPORT config.h token? That way there could be a --use-xdg to force use of xdg_user_dir_lookup regardless of platform for those who really want it. Sounds like here, you're trying to second-guess potential platform-missupport in libxdg...if a lower-level lib is broken, then *they* should be get it fixed so it propagates correctly (and more importantly, consistently) into all who use that lib.
This should probably be part of the GUserDirectory docs: + * The #GUserDirectory enumeration can be extended at later date. Not every + * platform has a directory for every logical id in the #GUserDirectory + * enumeration. Wrt to the caching, I'd prefer if we did the same thing we do e.g. for icon themes in gtk: Compare the actual mtime of the file, and reload it if it changed, but not stat the file more often than some small constant. the icon theme code uses 5 seconds, which may be a little short, but the 15 minutes you use here look a bit too long.
(In reply to comment #17) > This should probably be part of the GUserDirectory docs: > > + * The #GUserDirectory enumeration can be extended at later date. Not every > + * platform has a directory for every logical id in the #GUserDirectory > + * enumeration. done. > Wrt to the caching, I'd prefer if we did the same thing we do e.g. for icon > themes in gtk: > > Compare the actual mtime of the file, and reload it if it changed, but not stat > the file more often than some small constant. the icon theme code uses 5 > seconds, > which may be a little short, but the 15 minutes you use here look a bit too > long. done this too. the cache if G_OS_UNIX && !HAVE_CARBON is expired if the mtime of the configuration file has changed, but stat() is invoked only if more than five seconds are passed since the last check; on the other platforms, the cache expires automatically after 15 minutes because we don't have notification of user changes.
Created attachment 89234 [details] [review] [PATCH] Retrieve special folders via logical id This adds the g_get_user_special_dir() function. This function returns the full path for "special" directories, depending on the platform, using a logical ID. On G_OS_WIN, SHGetSpecialFolderLocation() is used; on G_OS_UNIX, the user configuration file defined by the xdg-user-dirs specification is parsed and the path is extracted from it, resolving the $HOME variable if needed; on OS X, Carbon is used to retrieve the special directories. Each path found is cached the first time it's retrieved. If using the XDG user directories, the cache is expired when the configuration file mtime changes (stat() on the configuration file is called only if 5 seconds are elapsed since the last check). On the other platforms, where we don't have any notification of user changes the cache is expired after 15 minutes. --- ChangeLog | 12 + configure.in | 18 ++- docs/reference/ChangeLog | 5 + docs/reference/glib/glib-sections.txt | 2 + docs/reference/glib/tmpl/misc_utils.sgml | 24 ++ glib/glib.symbols | 1 + glib/gutils.c | 369 ++++++++++++++++++++++++++++++ glib/gutils.h | 37 +++ tests/testglib.c | 8 + 9 files changed, 475 insertions(+), 1 deletions(-)
I don't see where maybe_expire_xdg_user_dirs updates g_user_special_dirs_mtime ? Also, don't you want to keep two times around 1) the mtime of user-dirs.dirs 2) the time you last stat'ed that file ?
(In reply to comment #20) > I don't see where maybe_expire_xdg_user_dirs updates g_user_special_dirs_mtime > ? the mtime is updated each time the cache string vector is created; if maybe_expire_xdg_user_dirs() nullified the cache because the st_mtime of the user-dirs.dirs file was greater than the cache mtime we keep, then st_mtime will be surely smaller than the value returned by the call to time() we do when we recreate the cache. as we update the cache mtime when the cache was last expired then it'll be also the last time the file was actually stat()-ed by maybe_expire_xdg_user_dirs(). > Also, don't you want to keep two times around > 1) the mtime of user-dirs.dirs > 2) the time you last stat'ed that file > ? I don't think it's needed, but I can rework the logic to be more explicit.
I think you need both times. If you only ever update the mtime when you reread the user-dirs.dirs file, then this check + if (now < g_user_special_dirs_mtime + 5) + return; will not trigger anymore once the 5 seconds have passed, so you'll stat the file in every may_expire_xdg_user_dirs() call after the 5 seconds, until the file changes and you get a fresh mtime. I think the effect we want is that a) we only reread the file if it actually changed b) we don't stat the file more often that every 5 seconds Your code achieves a), but it only achieves b) for the first 5 seconds after a reload. All of this analysis without actually running the code, so take it with a grain of salt...
looking some more at this, I think we want to handle the xdg-user-dirs case somewhat differently. The current code parses the user-dirs.dirs file once for each directory that is looked up; and if that directory is NULL, it parses the file over and over. Since we are caching, I'd say we should store all directories when we parse the file, and not reparse the file if a directory is NULL.
Created attachment 89307 [details] [review] alternative approach
Here is a patch that does things a bit differently: 1) it keeps two separate timestamps for the last stat time and the mtime 2) it reads all dirs in one go Note that I haven't tested it other than compiling the xdg version
Created attachment 89334 [details] [review] Combined patch This one combines the stuff by ebassi and mclasen and fixes the configure check for carbon.
2007-06-04 Matthias Clasen <mclasen@redhat.com> Add support for a number of special directories, as defined by the xdg-user-dirs specification. (#432651, Bastien Nocera, Emmanuele Bassi, Michael Natterer) * glib/glib.symbols: * glib/gutils.[hc]: Add the GUserDirectory enum and g_get_user_special_dir(), with implementations based on the xdg-user-dirs spec and on native interfaces for Win32 and Carbon. * configure.in: Add Carbon checks. * tests/tetsglib.c: Test g_get_user_special_dir().