Bug#490670: goplay: implement button "start this application"

Petter Reinholdtsen pere at hungry.com
Wed Feb 8 21:39:24 UTC 2012


[Petter Reinholdtsen]
> I really hope for some feedback on this design draft.

No objections so far, so I moved ahead.  Attached is a draft patch
that add support for starting programs from goplay.  It only work for
packages with only one desktop file in /usr/share/applications/, but
for those it will start the game/program.

This is the diffstat:

 Makefile.am                     |    2 
 configure.ac                    |    2 
 debian/changelog                |    8 
 debian/copyright                |    6 
 libxdgutils/Makefile.am         |   35 +
 libxdgutils/XDGAutostart.cpp    |  101 +++++
 libxdgutils/XDGAutostart.h      |   40 ++
 libxdgutils/XDGBasedir.cpp      |  147 +++++++
 libxdgutils/XDGBasedir.h        |   56 ++
 libxdgutils/XDGDesktopEntry.cpp |  781 ++++++++++++++++++++++++++++++++++++++++
 libxdgutils/XDGDesktopEntry.h   |  103 +++++
 libxdgutils/XDGMenu.cpp         |  315 ++++++++++++++++
 libxdgutils/XDGMenu.h           |   37 +
 libxdgutils/XDGUtils.cpp        |  112 +++++
 libxdgutils/XDGUtils.h          |   67 +++
 src/Makefile.am                 |    6 
 src/goplay.cpp                  |   55 ++
 src/pkgbrowser.cpp              |    6 
 src/pkgbrowser.h                |    4 
 src/ui.fld                      |    4 
 20 files changed, 1883 insertions(+), 4 deletions(-)

The libxdgutils directory is lifted directly from 
<URL: http://gitorious.org/xdg-autostart/ >.  The license is MIT.  I
hope this is acceptable.  The code is used to parse .desktop files and
start the program entry listed in this desktop file.

The code could be improved a lot, but is already functional.  There
are some debug output left in there to make it easier to figure out
what is going on.  If the desktop file is in a subdirectory (like
kde4), the code fail to discover that there is only one .desktop file.
If there are several, it fail to start any program.  And there is no
tooltip when hovering over the screen shot to tell the user what will
happen if the screenshot is clicked on.

Please apply this patch in a future version of goplay, or let me know
how you want the patch to change before applying it.
-- 
Happy hacking
Petter Reinholdtsen
-------------- next part --------------
diff -ur goplay-0.4/debian/changelog goplay-0.4-pere//debian/changelog
--- goplay-0.4/debian/changelog	2010-06-25 10:37:46.000000000 +0200
+++ goplay-0.4-pere//debian/changelog	2012-02-08 22:25:41.000000000 +0100
@@ -1,3 +1,11 @@
+goplay (0.4-1.1) UNRELEASED; urgency=low
+
+  * Non-maintainer upload.
+  * Add support for starting installed packages by clicking on the
+    screen shot (Closes: #490670).
+
+ -- Petter Reinholdtsen <pere at debian.org>  Wed, 08 Feb 2012 22:25:41 +0100
+
 goplay (0.4-1) UNRELEASED; urgency=low
 
   [ Peter De Wachter ]
diff -ur goplay-0.4/configure.ac goplay-0.4-pere//configure.ac
--- goplay-0.4/configure.ac	2010-06-25 10:37:51.000000000 +0200
+++ goplay-0.4-pere//configure.ac	2012-02-08 22:21:51.000000000 +0100
@@ -15,6 +15,7 @@
 AC_PROG_CXXCPP
 AM_PROG_CC_STDC
 AC_HEADER_STDC
+AC_PROG_RANLIB
 
 dnl Use libtool
 AM_PROG_LIBTOOL
@@ -31,6 +32,7 @@
 
 AC_CONFIG_FILES([
 Makefile
+libxdgutils/Makefile
 src/Makefile
 ])
 AC_OUTPUT
diff -ur goplay-0.4/debian/copyright goplay-0.4-pere//debian/copyright
--- goplay-0.4/debian/copyright	2010-06-25 10:25:32.000000000 +0200
+++ goplay-0.4-pere//debian/copyright	2012-02-08 22:21:36.000000000 +0100
@@ -21,6 +21,11 @@
 Copyright: ? 2007, 2008, Miriam Ruiz <little_miry at yahoo.es>
 License: GPL-2+
 
+Files: libxdgutils/*
+Copyright: ? 2010, 2011, Paulo Zanoni <przanoni at gmail.com>
+License: MIT
+Source: http://gitorious.org/xdg-autostart/
+
 License: GPL-2+
  This package is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
@@ -52,4 +57,25 @@
  be misrepresented as being the original software.
 
  3. This notice may not be removed or altered from any source
- distribution.
\ Intet linjeskift ved filafslutning
+ distribution.
+
+License: MIT
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the "Software"),
+ to deal in the Software without restriction, including without limitation
+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ and/or sell copies of the Software, and to permit persons to whom the
+ Software is furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice (including the next
+ paragraph) shall be included in all copies or substantial portions of the
+ Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ DEALINGS IN THE SOFTWARE.
+
diff -ur goplay-0.4/Makefile.am goplay-0.4-pere//Makefile.am
--- goplay-0.4/Makefile.am	2008-04-23 13:50:19.000000000 +0200
+++ goplay-0.4-pere//Makefile.am	2012-02-08 22:21:51.000000000 +0100
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = src .
+SUBDIRS = libxdgutils src .
 
 man_MANS = goplay.1
 
diff -ur goplay-0.4/src/goplay.cpp goplay-0.4-pere//src/goplay.cpp
--- goplay-0.4/src/goplay.cpp	2010-06-25 10:35:23.000000000 +0200
+++ goplay-0.4-pere//src/goplay.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -42,6 +42,7 @@
 #include <xapian.h>
 
 #include <iostream>
+#include <fstream>
 #include <cmath>
 
 #ifdef USE_GETTEXT
@@ -58,6 +59,8 @@
 
 #include <string.h>
 
+#include "libxdgutils/XDGDesktopEntry.h"
+
 namespace std {
 
 template<typename TAG, typename _Traits>
@@ -322,6 +325,55 @@
 	UpdateUILists(ui);
 }
 
+static void StartPackage(const std::string pkgname)
+{
+	// find desktop file, fork, exec
+  
+	std::vector<std::string> desktoplist;
+
+	std::string pkglistfile =
+	  std::string("/var/lib/dpkg/info/") + pkgname + ".list";
+	std::ifstream pkglist(pkglistfile.c_str());
+	if (pkglist.is_open())
+	  {
+	    while ( pkglist.good() )
+	      {
+		std::string line;
+		std::getline (pkglist,line);
+		if (0 == line.find("/usr/share/applications/")
+		    && 0 < line.find(".desktop")) {
+		  std::cout << line << endl;
+		  desktoplist.push_back(line);
+		}
+	      }
+	    pkglist.close();
+	  }
+	else
+	  std::cout << "Unable to open file" << std::endl; 
+	if (1 == desktoplist.size()) {
+	  std::string desktopfile(desktoplist.back());
+	  std::cout << "Running " << desktopfile << endl;
+	  XDGDesktopEntry entry(desktopfile.c_str());
+	  entry.run("");
+	} else {
+	  std::cout << "Not only one desktop file" << std::endl; 
+	}
+}
+
+static void CallBackScreenshotClick(Fl_Round_Button*, void *data)
+{
+	printf("CallBackScreenshotClick\n"); fflush(stdout);
+	GamesUI& ui = *static_cast<GamesUI*>(data);
+	PackageBrowser *ResultsBrowser = ui.ResultsBrowser;
+	std::string pkgname = ResultsBrowser->pkg_selected();
+	PackageRecord rec(ui.engine->apt().rawRecord(pkgname));
+
+	bool isinstalled = true; // FIXME find way to figure this out
+	if (isinstalled) {
+		StartPackage(pkgname);
+	}
+}
+
 #if 0
 static void OnResultsBrowserClick(Fl_Browser*, void *data)
 {
@@ -466,9 +518,12 @@
 		mainui.InstalledOrNot->value(1); mainui.InstalledOrNot->redraw();
 		mainui.engine->setInstalledFilter(Engine::ANY);
 
+		mainui.ScreenshotWidget->callback((Fl_Callback*)CallBackScreenshotClick, &mainui);
 		mainui.Screenshot(new Fl_PNG_Image(FILE_NO_SCREENSHOT));
 		mainui.AboutView->load(HTMLDIR "/about.en.html");
 
+		
+
 		UpdateUILists(mainui);
 
 		window->show(argc, (char**)argv);
diff -ur goplay-0.4/src/Makefile.am goplay-0.4-pere//src/Makefile.am
--- goplay-0.4/src/Makefile.am	2010-06-25 10:46:11.000000000 +0200
+++ goplay-0.4-pere//src/Makefile.am	2012-02-08 22:21:51.000000000 +0100
@@ -24,7 +24,11 @@
 	windows.cpp \
 	aux.cpp \
 	goplay.cpp
-goplay_LDFLAGS = $(LIBEPT_LIBS) `fltk-config --ldflags --use-images`
+goplay_LDFLAGS = \
+	`fltk-config --ldflags --use-images`
+goplay_LDADD = \
+	../libxdgutils/libxdgutils.a \
+	$(LIBEPT_LIBS)
 
 manpage_SOURCES = manpage.cpp
 manpage_LDADD = $(LIBEPT_LIBS)
diff -ur goplay-0.4/src/pkgbrowser.cpp goplay-0.4-pere//src/pkgbrowser.cpp
--- goplay-0.4/src/pkgbrowser.cpp	2010-06-25 10:32:57.000000000 +0200
+++ goplay-0.4-pere//src/pkgbrowser.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -155,6 +155,7 @@
 	{
 		int n = VersatileBrowser::lineno(p);
 		void *data = VersatileBrowser::data(n);
+		pkgname = (const char*) data;
 		//printf("  #%d : \"%s\"\n", n, (const char *)data);
 		//fflush(stdout);
 
@@ -510,3 +511,8 @@
 	}
 	return ret;
 }
+
+const std::string PackageBrowser::pkg_selected(void) const
+{
+  return pkgname;
+}
diff -ur goplay-0.4/src/pkgbrowser.h goplay-0.4-pere//src/pkgbrowser.h
--- goplay-0.4/src/pkgbrowser.h	2010-06-25 09:50:28.000000000 +0200
+++ goplay-0.4-pere//src/pkgbrowser.h	2012-02-08 22:21:51.000000000 +0100
@@ -32,6 +32,8 @@
 #include <FL/Fl_Help_View.H>
 #include <FL/fl_draw.H>
 
+#include <string>
+
 //
 // Demonstrate how to derive a class extending Fl_Browser with interactively resizable columns
 // erco 1.10 12/09/2005
@@ -202,6 +204,7 @@
 public:
 	PackageBrowser(int x, int y, int w, int h, const char *l = NULL);
 	virtual ~PackageBrowser();
+	const std::string pkg_selected(void) const;
 
 protected:
 	virtual void item_select(void *p, int s=1);
@@ -212,6 +215,7 @@
 private:
 	const unsigned int num_bar_images;
 	Fl_Image *bar_images[2*2];
+	std::string pkgname;
 };
 
 class PackageView : public Fl_Help_View
diff -ur goplay-0.4/src/ui.fld goplay-0.4-pere//src/ui.fld
--- goplay-0.4/src/ui.fld	2010-06-25 09:50:28.000000000 +0200
+++ goplay-0.4-pere//src/ui.fld	2012-02-08 22:21:51.000000000 +0100
@@ -63,10 +63,12 @@
   Function {Screenshot(Fl_Image *img)} {open return_type void
   } {
     code {Fl_Image *i=ScreenshotWidget->image();
+  Fl_Callback*callback = ScreenshotWidget->callback();
   ScreenshotWidget->image(img);
   if (i != NULL) {
     delete i;
   }
+  ScreenshotWidget->callback(callback);
   ScreenshotWidget->redraw();} {}
   }
   Function {CreateMainWindow()} {open
@@ -121,7 +123,7 @@
           image {../data/icons/Icon32.png} xywh {340 20 35 35} box NO_BOX
         }
       }
-      Fl_Box ScreenshotWidget {
+      Fl_Button ScreenshotWidget {
         xywh {385 15 320 240} box FLAT_BOX color 32 selection_color 32
       }
       Fl_Browser ResultsBrowser {
diff -urN goplay-0.4/libxdgutils/Makefile.am goplay-0.4-pere//libxdgutils/Makefile.am
--- goplay-0.4/libxdgutils/Makefile.am	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/Makefile.am	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,35 @@
+# Copyright (c) 2011 Paulo Zanoni
+#
+# Permission is hereby granted, free of charge, to any person obtaining a
+# copy of this software and associated documentation files (the "Software"),
+# to deal in the Software without restriction, including without limitation
+# the rights to use, copy, modify, merge, publish, distribute, sublicense,
+# and/or sell copies of the Software, and to permit persons to whom the
+# Software is furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice (including the next
+# paragraph) shall be included in all copies or substantial portions of the
+# Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+# DEALINGS IN THE SOFTWARE.
+
+noinst_LIBRARIES = libxdgutils.a
+libxdgutils_a_SOURCES = \
+	XDGAutostart.h \
+	XDGAutostart.cpp \
+	XDGBasedir.h \
+	XDGBasedir.cpp \
+	XDGDesktopEntry.h \
+	XDGDesktopEntry.cpp \
+	XDGMenu.h \
+	XDGMenu.cpp \
+	XDGUtils.h \
+	XDGUtils.cpp
+
+INCLUDES = -I.. $(LIBEPT_CFLAGS) -Wall -Werror -ggdb
diff -urN goplay-0.4/libxdgutils/XDGAutostart.cpp goplay-0.4-pere//libxdgutils/XDGAutostart.cpp
--- goplay-0.4/libxdgutils/XDGAutostart.cpp	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGAutostart.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstring>
+#include <iostream>
+#include <set>
+
+extern "C" {
+#include <sys/types.h>
+#include <dirent.h>
+}
+
+#include "XDGAutostart.h"
+#include "XDGUtils.h"
+
+static bool strEndsWith(const char *s, const char *substr)
+{
+    size_t i;
+    size_t s_len = strlen(s);
+    size_t substr_len = strlen(substr);
+
+    if (substr_len > s_len)
+	return false;
+
+    for(i = s_len - substr_len; i < s_len; i++)
+	if (s[i] != substr[i - (s_len - substr_len)])
+	    return false;
+
+    return true;
+}
+
+
+void XDGAutostart::run(const char *environment)
+{
+    std::list<std::string> autostartDirs;
+    std::list<std::string>::iterator it;
+
+    std::set<std::string> autostartFiles;
+    DIR *autostartDir;
+    struct dirent *dirEntry;
+
+    autostartDirs = XDGBasedir::findConfigFile("autostart");
+
+    for(it = autostartDirs.begin(); it != autostartDirs.end(); ++it) {
+
+	autostartDir = opendir(it->c_str());
+	if (!autostartDir) {
+	    std::cerr << "Error opening " << *it << " directory\n";
+	    return;
+	}
+
+	while ((dirEntry = readdir(autostartDir))) {
+	    if (strEndsWith(dirEntry->d_name, ".desktop")) {
+		XDGUtils::log(XDGUtils::Verbose, "File found: %s\n",
+			      dirEntry->d_name);
+
+		if (autostartFiles.count(dirEntry->d_name) == 0) {
+		    std::string fullFileName = *it + std::string("/") +
+			std::string(dirEntry->d_name);
+		    runDesktopFile(fullFileName.c_str(), environment);
+		    autostartFiles.insert(dirEntry->d_name);
+		} else {
+		    std::cout << "not running: repeated\n";
+		}
+	    }
+	}
+
+	if (closedir(autostartDir) != 0)
+	    std::cerr << "Error: failed to close dir " << it->c_str() << "\n";
+    }
+}
+
+int XDGAutostart::runDesktopFile(const char *fileName,
+				 const char *environment)
+{
+    XDGDesktopEntry entry(fileName);
+    if (entry.showInEnvironment(environment))
+	return entry.run("");
+
+    return 0;
+}
diff -urN goplay-0.4/libxdgutils/XDGAutostart.h goplay-0.4-pere//libxdgutils/XDGAutostart.h
--- goplay-0.4/libxdgutils/XDGAutostart.h	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGAutostart.h	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef XDG_AUTOSTART_H
+#define XDG_AUTOSTART_H
+
+#include "XDGBasedir.h"
+#include "XDGDesktopEntry.h"
+
+class XDGAutostart
+{
+    public:
+	static void run(const char *environment);
+
+    private:
+	static int runDesktopFile(const char *fileName,
+				  const char *environment);
+};
+
+#endif
diff -urN goplay-0.4/libxdgutils/XDGBasedir.cpp goplay-0.4-pere//libxdgutils/XDGBasedir.cpp
--- goplay-0.4/libxdgutils/XDGBasedir.cpp	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGBasedir.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+#include "XDGBasedir.h"
+#include "XDGUtils.h"
+
+// XXX: spec mentions these directories, but hardcodes /etc and /usr in the
+//      specification of default directories to be used! Fix spec?
+#ifndef SYSCONFDIR
+#define SYSCONFDIR "/etc"
+#endif
+#ifndef DATADIR
+#define DATADIR "/usr/share"
+#endif
+
+std::string XDGBasedir::getVariable(XDGVariables var)
+{
+    const char *varName;
+    switch(var) {
+	case XDG_DATA_HOME:
+	    varName = "XDG_DATA_HOME";
+	    break;
+	case XDG_CONFIG_HOME:
+	    varName = "XDG_CONFIG_HOME";
+	    break;
+	case XDG_DATA_DIRS:
+	    varName = "XDG_DATA_DIRS";
+	    break;
+	case XDG_CONFIG_DIRS:
+	    varName = "XDG_CONFIG_DIRS";
+	    break;
+	case XDG_CACHE_HOME:
+	    varName = "XDG_CACHE_HOME";
+	    break;
+	case XDG_RUNTIME_DIR:
+	    varName = "XDG_RUNTIME_DIR";
+	    break;
+	default:
+	    std::cerr << "Error: undefined variable!\n";
+	    return "";
+    }
+
+    char *varValue = getenv(varName);
+
+    if (varValue)
+	return varValue;
+
+    char *home = NULL;
+    if (var == XDG_DATA_HOME || var == XDG_CONFIG_HOME ||
+	var == XDG_CACHE_HOME) {
+	home = getenv("HOME");
+	if (!home) {
+	    std::cerr << "Error: $HOME is not set!\n";
+	    return "";
+	}
+    }
+
+    std::string ret;
+    switch(var) {
+	case XDG_DATA_HOME:
+	    ret = std::string(home) + "/.local/share";
+	    break;
+	case XDG_CONFIG_HOME:
+	    ret = std::string(home) + "/.config";
+	    break;
+	case XDG_DATA_DIRS:
+	    ret = "/usr/local/share/:/usr/share/";
+	    break;
+	case XDG_CONFIG_DIRS:
+	    // XXX: spec doesn't mention $sysconfdir here!
+	    ret = "/etc/xdg";
+	    break;
+	case XDG_CACHE_HOME:
+	    ret = std::string(home) + "/.cache";
+	    break;
+	case XDG_RUNTIME_DIR:
+	    // "If $XDG_RUNTIME_DIR is not set applications should fall back to
+	    // a replacement directory with similar capabilities and print a
+	    // warning message"
+	    std::cout << "Warning: XDG_RUNTIME_DIR not set\n";
+	    return "";
+    }
+    return ret;
+}
+
+bool XDGBasedir::validRuntimeDir()
+{
+    // XXX: check the spec! there are a lot of things to validate here
+    return true;
+}
+
+std::list<std::string> XDGBasedir::findFile(const char *fileName,
+					    const std::string& userDir,
+					    const std::string& systemDirs)
+{
+    std::list<std::string> ret;
+
+    ret.push_back(std::string(userDir + '/' + fileName));
+
+    char *path = strdup(systemDirs.c_str());
+    char *tokStr, *tokPtr;
+    ITERATE_STRTOK_R(path, ":", &tokPtr, tokStr) {
+	std::string fullPath(std::string(tokStr) + '/' + fileName);
+	if (XDGUtils::fileExists(fullPath.c_str()))
+	    ret.push_back(fullPath);
+    }
+    free(path);
+
+    return ret;
+}
+
+std::list<std::string> XDGBasedir::findDataFile(const char *fileName)
+{
+    return findFile(fileName, getVariable(XDG_DATA_HOME),
+		    getVariable(XDG_DATA_DIRS));
+}
+
+std::list<std::string> XDGBasedir::findConfigFile(const char *fileName)
+{
+    return findFile(fileName, getVariable(XDG_CONFIG_HOME),
+		    getVariable(XDG_CONFIG_DIRS));
+}
+
diff -urN goplay-0.4/libxdgutils/XDGBasedir.h goplay-0.4-pere//libxdgutils/XDGBasedir.h
--- goplay-0.4/libxdgutils/XDGBasedir.h	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGBasedir.h	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef XDG_BASEDIR_H
+#define XDG_BASEDIR_H
+
+#include <list>
+#include <string>
+
+class XDGBasedir
+{
+    public:
+	enum XDGVariables {
+	    XDG_DATA_HOME,
+	    XDG_CONFIG_HOME,
+	    XDG_DATA_DIRS,
+	    XDG_CONFIG_DIRS,
+	    XDG_CACHE_HOME,
+	    XDG_RUNTIME_DIR
+	};
+
+	static std::string getVariable(XDGVariables var);
+
+	// checks if XDG_RUNTIME_DIR is compliant with the specification
+	static bool validRuntimeDir();
+
+	static std::list<std::string> findDataFile(const char *fileName);
+	static std::list<std::string> findConfigFile(const char *fileName);
+
+    private:
+	static std::list<std::string> findFile(const char *fileName,
+					       const std::string& userDir,
+					       const std::string& systemDirs);
+};
+
+#endif
diff -urN goplay-0.4/libxdgutils/XDGDesktopEntry.cpp goplay-0.4-pere//libxdgutils/XDGDesktopEntry.cpp
--- goplay-0.4/libxdgutils/XDGDesktopEntry.cpp	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGDesktopEntry.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,781 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+
+#include <cassert>
+#include <cstdio>
+#include <cstring>
+#include <cstdlib>
+
+#include <iostream>
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+}
+
+#include "XDGDesktopEntry.h"
+#include "XDGUtils.h"
+#include "XDGMenu.h"
+
+XDGDesktopEntry::XDGEntryDescription XDGDesktopEntry::registeredEntries[] = {
+    // Note: "Type" is special and treated in a different way
+
+    // name,           valueType,       reqrd, type,        dpctd, valdValFnc
+    //{ "Type",         XDGString,       true,  All,         false, NULL },
+    { "Version",        XDGString,       false, All,         false, NULL },
+    { "Name",           XDGLocalestring, true,  All,         false, NULL },
+    { "GenericName",    XDGLocalestring, false, All,         false, NULL },
+    { "NoDisplay",      XDGBoolean,      false, All,         false, NULL },
+    { "Comment",        XDGLocalestring, false, All,         false, NULL },
+    { "Icon",           XDGLocalestring, false, All,         false, NULL },
+    { "Hidden",         XDGBoolean,      false, All,         false, NULL },
+    { "OnlyShowIn",     XDGStrings,      false, All,         false,
+	XDGMenu::validEnvironments },
+    { "NotShowIn",      XDGStrings,      false, All,         false,
+	XDGMenu::validEnvironments },
+    { "TryExec",        XDGString,       false, Application, false, NULL },
+    { "Exec",           XDGString,       true,  Application, false,
+	validExec },
+    { "Path",           XDGString,       false, Application, false, NULL },
+    { "Terminal",       XDGBoolean,      false, Application, false, NULL },
+    { "MimeType",       XDGStrings,      false, Application, false, NULL },
+    { "Categories",     XDGStrings,      false, Application, false,
+	XDGMenu::validCategories },
+    { "StartupNotify",  XDGBoolean,      false, Application, false, NULL },
+    { "StartupWMClass", XDGString,       false, Application, false, NULL },
+    { "URL",            XDGString,       true,  Link,        false, NULL },
+
+    // Old KDE stuff:
+    // XXX: their valueType is not specified in the spec!
+    //      I'm not sure they're actually considered deprecated...
+    { "ServiceTypes",      XDGLocalestring, false, All,         false, NULL },
+    { "DocPath",           XDGLocalestring, false, All,         false, NULL },
+    { "Keywords",          XDGLocalestring, false, All,         false, NULL },
+    { "InitialPreference", XDGLocalestring, false, All,         false, NULL },
+    { "Dev",               XDGString,       false, FSDevice,    false, NULL },
+    { "FSType",            XDGString,       false, FSDevice,    false, NULL },
+    { "MountPoint",        XDGString,       false, FSDevice,    false, NULL },
+    { "ReadOnly",          XDGBoolean,      false, FSDevice,    false, NULL },
+    { "UnmountIcon",       XDGLocalestring, false, FSDevice,    false, NULL },
+
+    // Really deprecated items
+    // valueType not specified...
+    { "Patterns",        XDGLocalestring, false, MimeType,    true,  NULL },
+    { "DefaultApp",      XDGLocalestring, false, MimeType,    true,  NULL },
+    { "Encoding",        XDGLocalestring, false, All,         true,  NULL },
+    { "MiniIcon",        XDGLocalestring, false, All,         true,  NULL },
+    { "TerminalOptions", XDGLocalestring, false, All,         true,  NULL },
+    { "Protocols",       XDGLocalestring, false, All,         true,  NULL },
+    { "Extensions",      XDGLocalestring, false, All,         true,  NULL },
+    { "BinaryPattern",   XDGLocalestring, false, All,         true,  NULL },
+    { "MapNotify",       XDGLocalestring, false, All,         true,  NULL },
+    { "SwallowTitle",    XDGLocalestring, false, All,         true,  NULL },
+    { "SwallowExec",     XDGString,       false, All,         true,  NULL },
+    { "SortOrder",       XDGStrings,      false, All,         true,  NULL },
+    { "FilePattern",     XDGLocalestring, false, All,         true,  NULL },
+
+
+    // End of list
+    { NULL,             XDGStrings,      false, All,         false, NULL },
+    //{ "",    XDGLocalestring, false, All, false, NULL },
+
+    // XXX: Keys that have a specific set of values (so we need to implement
+    //      validValueFunc):
+    //      - Encoding (deprecated)
+    // XXX: FilePattern actually had its own "regex" type
+    // XXX: We're only 1.0-compliant, so why bother registering deprecated stuff
+    //      here? Maybe we should just fail?
+};
+
+
+XDGDesktopEntry::XDGDesktopEntry(const char *fileName)
+    : valid_(false),
+      fileName_(NULL),
+      fileContent_(NULL),
+      entries_(NULL)
+{
+    struct stat statBuffer;
+    int fd;
+
+    fileName_ = strdup(fileName);
+    assert(fileName_);
+
+    if (stat(fileName_, &statBuffer) != 0) {
+	std::cerr << "Error: can't stat " << fileName_ << "\n";
+	return;
+    }
+
+    // We're going to slurp the whole file, add a few '\0's and point structs to
+    // it.
+    fileContent_ = (char *)malloc(statBuffer.st_size+1);
+    assert(fileContent_);
+    fileContent_[statBuffer.st_size] = '\0';
+
+    fd = open(fileName_, O_RDONLY);
+    if (fd == -1) {
+	std::cerr << "Error: can't open " << fileName_ << "\n";
+	return;
+    }
+
+    ssize_t ret, readed = 0;
+    while (readed < statBuffer.st_size) {
+	ret = read(fd, fileContent_, statBuffer.st_size - readed);
+	if (ret == -1) {
+	    std::cerr << "Error: can't read " << fileName_ << "\n";
+	    return;
+	}
+	readed += ret;
+    }
+
+    if (close(fd) == -1) {
+	std::cerr << "Error: can't close " << fileName_ << "\n";
+	return;
+    }
+
+    XDGUtils::log(XDGUtils::Debug, "File:\n%s\n", fileContent_);
+
+    char *line;
+    int lineNumber = 1;
+    bool desktopEntryGroupFound = false;
+    for(int i = 0; fileContent_[i] != '\0'; i++, lineNumber++) {
+	line = &fileContent_[i];
+
+	if (line[0] == '\n') {
+	    continue;
+	}
+
+	if (line[0] == '#') {
+	    for(; fileContent_[i] != '\n'; i++)
+		;
+	    continue;
+	}
+
+	if (line[0] == '[') {
+	    if (desktopEntryGroupFound) {
+		// We don't care about reading other groups for now.
+		// Later we might parse them and check for correct syntax
+		break;
+	    } else {
+		if (strncmp(line, "[Desktop Entry]\n", 16) == 0) {
+		    desktopEntryGroupFound = true;
+		    for(; fileContent_[i] != '\n'; i++)
+			;
+		    continue;
+		} else {
+		    std::cerr << fileName_ << " error: first group must be "
+			      << "[Desktop Entry]\n";
+		    return;
+		}
+	    }
+	}
+
+	// processEntry will mess with the line, so we better advance now
+	for(; fileContent_[i] != '\n'; i++)
+	    ;
+	if (!processEntry(line)) {
+	    std::cerr << fileName_ << " error: invalid entry at line "
+		      << lineNumber << "\n";
+	    return;
+	}
+    }
+
+    validateEntries();
+}
+
+XDGDesktopEntry::~XDGDesktopEntry()
+{
+    free(fileName_);
+    free(fileContent_);
+    struct XDGEntry *pEntry = entries_;
+    struct XDGEntry *prev;
+
+    while (pEntry != NULL) {
+	prev = pEntry;
+	pEntry = pEntry->next;
+	free(prev);
+    }
+}
+
+bool XDGDesktopEntry::processEntry(char *line)
+{
+    unsigned int i, keyEnd = 0, valueBegin = 0;
+    unsigned int localeBegin = 0, localeEnd = 0;
+    char lastChar;
+
+    struct XDGEntry *entry = (struct XDGEntry*)malloc(sizeof(struct XDGEntry));
+    assert(entry);
+
+    // Look for the key
+    for(i = 0; line[i] != '\n' && line[i] != '\0'; i++) {
+	if (!isalnum(line[i]) && line[i] != '-') {
+	    keyEnd = i;
+	    break;
+	}
+    }
+
+    if (keyEnd == 0) {
+	free(entry);
+	return false;
+    }
+
+    lastChar = line[keyEnd];
+    line[keyEnd] = '\0';
+    entry->key = &line[0];
+    XDGUtils::log(XDGUtils::Debug, "key[%s]\n", entry->key);
+    i++;
+
+    // Check for localized entries
+    if (lastChar == '[') {
+	localeBegin = i;
+	for(; line[i] != '\n' && line[i] != '\0'; i++) {
+	    if (line[i] == ']') {
+		localeEnd = i;
+		i++;
+		break;
+	    }
+	}
+	if (localeEnd != 0) {
+	    lastChar = line[localeEnd];
+	    line[localeEnd] = '\0';
+	    entry->locale = &line[localeBegin];
+	} else {
+	    free(entry);
+	    return false;
+	}
+    } else {
+	entry->locale = NULL;
+    }
+
+    if (entry->locale)
+	XDGUtils::log(XDGUtils::Debug, "locale[%s]\n", entry->locale);
+    else
+	XDGUtils::log(XDGUtils::Debug, "locale[NULL]\n");
+
+    // Now eat spaces until the '=' char
+    if (lastChar != '=') {
+	for(; line[i] != '\n' && line[i] != '\0'; i++) {
+	    if (!isspace(line[i]))
+		break;
+	}
+	if (line[i] != '=') {
+	    std::cerr << fileName_ << " error: '=' expected, but found '"
+		      << line[i] << "'\n";
+	    free(entry);
+	    return false;
+	} else {
+	    i++;
+	}
+    }
+
+    // Now eat spaces until the value starts
+    for(; line[i] != '\n' && line[i] != '\0'; i++) {
+	if (!isspace(line[i])) {
+	    valueBegin = i;
+	    break;
+	}
+    }
+
+    if (line[i] == '\n' || line[i] == '\0' ||
+	line[valueBegin] == '\n' || line[valueBegin] == '\0') {
+	// Although the spec doesn't mention, there are desktop files with
+	// empty values
+	entry->value = NULL;
+    } else {
+	entry->value = &line[valueBegin];
+	for(; line[i] != '\n' && line[i] != '\0'; i++)
+	    ;
+	line[i] = '\0';
+    }
+
+    if (entry->value)
+	XDGUtils::log(XDGUtils::Debug, "value[%s]\n", entry->value);
+    else
+	XDGUtils::log(XDGUtils::Debug, "value[NULL]\n");
+
+    return storeEntry(entry);
+}
+
+bool XDGDesktopEntry::storeEntry(struct XDGEntry *entry)
+{
+    struct XDGEntry *pEntry;
+
+    entry->next = NULL;
+
+    if (entries_ == NULL) {
+	entries_ = entry;
+	return true;
+    }
+    pEntry = entries_;
+    while (pEntry->next != NULL) {
+	if (strcmp(entry->key, pEntry->key) == 0) {
+	    if (entry->locale == NULL && pEntry->locale == NULL) {
+		std::cerr << fileName_ << " error: key " << entry->key
+			  << "already stored!\n";
+		return false;
+	    }
+
+	    if (!(entry->locale == NULL || pEntry->locale == NULL)) {
+		if (strcmp(entry->locale, pEntry->locale) == 0) {
+		    std::cerr << fileName_ << " error: key " << entry->key
+			      << "[" << entry->locale  << "] already stored!\n";
+		    return false;
+		}
+	    }
+
+	}
+	pEntry = pEntry->next;
+    }
+
+    pEntry->next = entry;
+    return true;
+}
+
+bool XDGDesktopEntry::getEntry(const char *key, const char *locale,
+			       const char **value)
+{
+    struct XDGEntry *pEntry;
+
+    pEntry = entries_;
+    while (pEntry != NULL) {
+	if (strcmp(pEntry->key, key) == 0) {
+	    if ((pEntry->locale == NULL && locale == NULL) ||
+		(strcmp(pEntry->locale, locale) == 0)) {
+		*value = pEntry->value;
+		return true;
+	    }
+	}
+	pEntry = pEntry->next;
+    }
+    return false;
+}
+
+void XDGDesktopEntry::validateEntries()
+{
+    int i;
+    char *key = NULL;
+    char *locale;
+    const char *value;
+    XDGTypeMask type;
+
+    valid_ = false;
+
+    if (!getEntry("Type", NULL, &value)) {
+	std::cerr << fileName_ << " error: \"Type\" key not found!\n";
+	return;
+    } else {
+	if (strcmp(value, "Application") == 0)
+	    type = Application;
+	else if (strcmp(value, "Link") == 0)
+	    type = Link;
+	else if (strcmp(value, "Directory") == 0)
+	    type = Directory;
+	else if (strcmp(value, "ServiceType") == 0)
+	    type = ServiceType;
+	else if (strcmp(value, "Service") == 0)
+	    type = Service;
+	else if (strcmp(value, "FSDevice") == 0)
+	    type = FSDevice;
+	else if (strcmp(value, "MimeType") == 0) {
+	    type = MimeType;
+	    std::cerr << fileName_
+		      << " error: type \"MimeType\" is deprecated!\n";
+	} else {
+	    std::cerr << fileName_ << " error: invalid type!\n";
+	    return;
+	}
+    }
+
+    // For each key we have, compare against the registered ones
+    struct XDGEntry *pEntry;
+    for(pEntry = entries_; pEntry != NULL; pEntry = pEntry->next) {
+	key = pEntry->key;
+	locale = pEntry->locale;
+	value = pEntry->value;
+
+	if ((key[0] == 'X' && key[1] == '-') || (strcmp(key, "Type") == 0))
+	    continue;
+
+	bool found = false;
+	for(i = 0; registeredEntries[i].name != NULL; i++) {
+	    if (strcmp(registeredEntries[i].name, key) != 0)
+		continue;
+
+	    found = true;
+
+	    if (!(registeredEntries[i].type & type)) {
+		std::cerr << fileName_ << " error: key " << key
+			  << " invalid for this type\n";
+		return;
+	    }
+
+	    if (locale != NULL &&
+		registeredEntries[i].valueType != XDGLocalestring) {
+		std::cerr << fileName_ << " error: only localestring can "
+			  << "have localized values\n";
+		return;
+	    }
+
+	    if (!checkValueType(value, registeredEntries[i].valueType)) {
+		std::cerr << fileName_ << " error: invalid value "
+			  << value << " for type\n";
+		return;
+	    }
+
+	    if (registeredEntries[i].deprecated)
+		std::cerr << fileName_ << " warning: key " << key
+			  << " is deprecated\n";
+
+	    if (registeredEntries[i].validValueFunc != NULL) {
+		if (!(registeredEntries[i].validValueFunc)(value)) {
+		    std::cerr << fileName_ << " error: invalid value " << value
+			      << "\n";
+		    return;
+		}
+	    }
+
+	    break;
+	}
+	if (!found) {
+	    std::cerr << fileName_ << " error: key " << key
+		      << " is not registered\n";
+	    return;
+	}
+
+    }
+
+    // Now find missing required keys
+    for(i = 0; registeredEntries[i].name != NULL; i++) {
+	if (!registeredEntries[i].type & type)
+	    continue;
+
+	if (registeredEntries[i].required) {
+	    if (!getEntry(key, NULL, &value)) {
+		std::cerr << fileName_ << " error: key " << key
+			  << " entry required but not found\n";
+		return;
+	    }
+	}
+    }
+
+
+    if (getEntry("OnlyShowIn", NULL, &value) &&
+	getEntry("NotShowIn", NULL, &value)) {
+	std::cerr << fileName_ << " error: only one of OnlyShowIn or "
+		  << "NotShowIn is allowed\n";
+	return;
+    }
+
+    // XXX: this way, we only check for the "defined" entries. Anything else is
+    //      just ignored! Shouldn't we fail for unknown entries?
+
+
+    valid_ = true;
+}
+
+bool XDGDesktopEntry::checkValueType(const char *value, XDGEntryValueType type)
+{
+    unsigned int i;
+
+    // Allow empty values for now
+    if (!value)
+	return true;
+
+    if (type == XDGString) {
+	// "Values of type string may contain all ASCII characters except for
+	// control characters."
+	for(i = 0; value[i] != '\0'; i++)
+	    if (!isascii(value[i]) || iscntrl(value[i]))
+		return false;
+	return true;
+
+    } else if (type == XDGStrings) {
+	char *s, *tokPtr, *tokStr;
+	if (!XDGUtils::validSemicolonList(value))
+	    return false;
+
+	s = strdup(value);
+	ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
+	    if (!checkValueType(tokStr, XDGString)) {
+		free(s);
+		return false;
+	    }
+	}
+	free(s);
+	return true;
+
+    } else if (type == XDGLocalestring) {
+	// "Values of type localestring are user displayable, and are encoded in
+	// UTF-8."
+	// XXX: implement! check for displayability and utf-8!
+	return true;
+
+    } else if (type == XDGBoolean) {
+	// "Values of type boolean must either be the string true or false."
+	if (strcmp(value, "0") == 0 || strcmp(value, "1") == 0) {
+	    std::cerr << fileName_ << " warning: boolean values \"0\" and "
+		      << "\"1\" are deprecated!\n";
+	    return true;
+	}
+	if ((strcmp(value, "true") != 0) && (strcmp(value, "false") != 0))
+	    return false;
+	else
+	    return true;
+
+    } else if (type == XDGNumeric) {
+	// "Values of type numeric must be a valid floating point number as
+	// recognized by the %f specifier for scanf in the C locale."
+	float tmp;
+	if (sscanf(value, "%f", &tmp) != 1)
+	    return false;
+	else
+	    return true;
+
+    } else {
+	std::cerr << fileName_ << " error: invalid XDGEntryType\n";
+	return false;
+    }
+}
+
+bool XDGDesktopEntry::valid()
+{
+    return valid_;
+}
+
+bool XDGDesktopEntry::showInEnvironment(const char *environment)
+{
+    if (!valid_) {
+	XDGUtils::log(XDGUtils::Verbose,
+		      "%s error: invalid desktop file\n", fileName_);
+	return false;
+    }
+
+    const char *value;
+    char *s, *tokPtr, *tokStr;
+    if (getEntry("OnlyShowIn", NULL, &value)) {
+	s = strdup(value);
+	ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
+	    if (strcmp(tokStr, environment) == 0) {
+		free(s);
+		return true;
+	    }
+	}
+	XDGUtils::log(XDGUtils::Verbose, "%s not in OnlyShowIn list\n",
+		      fileName_);
+	free(s);
+	return false;
+    } else if (getEntry("NotShowIn", NULL, &value)) {
+	s = strdup(value);
+	ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
+	    if (strcmp(tokStr, environment) == 0) {
+		XDGUtils::log(XDGUtils::Verbose,
+			      "%s present in NotShowIn list\n", fileName_);
+		free(s);
+		return false;
+
+	    }
+	}
+	free(s);
+	return true;
+    }
+
+    return true;
+}
+
+int XDGDesktopEntry::run(const char *args)
+{
+    const char *value;
+
+    if (!valid_)
+	return 0;
+
+    XDGUtils::log(XDGUtils::Verbose, "%s: --> Running entry!\n", fileName_);
+    std::string executablePath;
+
+    // Test TryExec:
+    if (getEntry("TryExec", NULL, &value)) {
+	XDGUtils::log(XDGUtils::Verbose, "%s: TryExec: %s\n", fileName_, value);
+
+	if (value[0] == '/' || value[0] == '.') {
+	    if (!XDGUtils::fileExists(value)) {
+		XDGUtils::log(XDGUtils::Verbose,
+			      "%s error: TryExec failed: file %s not found\n",
+			      fileName_, value);
+		return 0;
+	    }
+	    executablePath = value;
+	} else {
+	    if (!XDGUtils::findFileInPath(value, executablePath)) {
+		XDGUtils::log(XDGUtils::Verbose,
+			      "%s error: TryExec failed: file %s not found\n",
+			      fileName_, value);
+		return 0;
+	    }
+	}
+	if (access(executablePath.c_str(), X_OK) == -1) {
+	    XDGUtils::log(XDGUtils::Verbose,
+			  "%s TryExec failed: file %s not executable\n",
+			  fileName_, value);
+	    return 0;
+	}
+	XDGUtils::log(XDGUtils::Verbose, "%s: -- TryExec success\n",
+		      fileName_);
+    }
+
+    // Now run the desktop file
+    getEntry("Exec", NULL, &value);
+
+    // XXX: For now, just remove the field codes...
+    std::string commandString;
+    for(unsigned int i = 0; value[i] != '\0'; i++) {
+	if (value[i] == '%') {
+	    i++;
+	    if (value[i] == 'i') {
+		// "The Icon key of the desktop entry expanded as two arguments,
+		// first --icon and then the value of the Icon key. Should not
+		// expand to any arguments if the Icon key is empty or missing."
+		const char *iconValue;
+		if (getEntry("Icon", NULL, &iconValue)) {
+		    commandString += std::string("-- icon ") + iconValue;
+		}
+	    } else if (value[i] == 'c') {
+		// "The translated name of the application as listed in the
+		// appropriate Name key in the desktop entry."
+		// XXX: implement
+	    } else if (value[i] == 'k') {
+		// "The location of the desktop file as either a URI (if for
+		// example gotten from the vfolder system) or a local filename
+		// or empty if no location is known."
+		// XXX: implement
+	    } else {
+		continue;
+	    }
+	}
+	commandString += value[i];
+    }
+
+    commandString += std::string(" ") + args;
+
+    return runCommand(commandString.c_str());
+}
+
+int XDGDesktopEntry::runCommand(const char *commandString)
+{
+    // XXX: use alloca instead of new?
+    unsigned int i, buf_i, j;
+    bool lookingForQuote = false;
+    pid_t pid;
+
+    // "buffer" contains consecutive strings separated by '\0's
+    char *buffer = (char *) alloca(strlen(commandString) * sizeof(char));
+    char *arg = &buffer[0];
+    unsigned int arguments = 0;
+
+    for(i = 0, buf_i = 0; commandString[i] != '\0'; i++) {
+	if (commandString[i] == '\\') {
+	    i++;
+	} else if (commandString[i] == ' ' && (!lookingForQuote)) {
+	    if (!(arg == &buffer[buf_i])) {
+		// Not two consecutive spaces
+		buffer[buf_i] = '\0';
+		arguments++;
+		buf_i++;
+		arg = &buffer[buf_i];
+	    }
+	} else if (commandString[i] == '"') {
+	    lookingForQuote = !lookingForQuote;
+	} else {
+	    buffer[buf_i] = commandString[i];
+	    buf_i++;
+	}
+    }
+    if (arg != &buffer[buf_i]) {
+	buffer[buf_i] = '\0';
+	arguments++;
+    }
+
+    if (lookingForQuote) {
+        std::cerr << "Error: missing quotes\n";
+        return 0;
+    }
+
+    if (arguments == 0) {
+        std::cerr << "Error: empty command\n";
+        return 0;
+    }
+
+    char **c_args = (char **) alloca(sizeof(char *) * arguments+1);
+    char *c_command;
+
+    c_command = buffer;
+    for(i = 0, j = 0; i < arguments; i++) {
+	c_args[i] = &buffer[j];
+	// Skip to the next argument:
+	for(; buffer[j] != '\0'; j++)
+	    ;
+	j++;
+    }
+    c_args[i] = NULL;
+
+    pid = fork();
+    if (pid == 0) {
+        execvp(c_command, c_args);
+        exit(0);
+    }
+
+    return pid;
+}
+
+
+bool XDGDesktopEntry::validExec(const char *execString)
+{
+    bool exclusiveFieldFound = false;
+    for(unsigned int i = 0; execString[i] != '\0'; i++) {
+	if (execString[i] == '%') {
+	    i++;
+	    if (execString[i] == 'f' || execString[i] == 'F' ||
+		execString[i] == 'u' || execString[i] == 'U') {
+		if (exclusiveFieldFound) {
+		    std::cerr << "Error: Exec fields %f, %F, %u "
+			         "or %U are mutually exclusive\n";
+		    return false;
+		} else {
+		    exclusiveFieldFound = true;
+		}
+	    } else if (execString[i] == 'd' || execString[i] == 'D' ||
+		       execString[i] == 'n' || execString[i] == 'N' ||
+		       execString[i] == 'v' || execString[i] == 'm' ) {
+		std::cerr << "Warning: deprecated Exec field code %"
+			  << execString[i] << "\n";
+	    } else if (execString[i] != 'i' && execString[i] != 'c' &&
+		       execString[i] != 'k') {
+		std::cerr << "Error: invalid Exec field %"
+			  << execString[i] << "\n";
+		return false;
+	    }
+	}
+    }
+    return true;
+}
diff -urN goplay-0.4/libxdgutils/XDGDesktopEntry.h goplay-0.4-pere//libxdgutils/XDGDesktopEntry.h
--- goplay-0.4/libxdgutils/XDGDesktopEntry.h	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGDesktopEntry.h	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef XDG_DESKTOP_ENTRY_H
+#define XDG_DESKTOP_ENTRY_H
+
+class XDGDesktopEntry
+{
+    // XXX: todo:
+    //      - improve checks
+    //      - check KDE stuff
+    //      - check X- extensions
+    //      - warn on unexistant keys
+    //      - don't allow localization for non localestring stuff
+    //      - implement a func to write a file
+    public:
+	XDGDesktopEntry(const char *fileName);
+	~XDGDesktopEntry();
+	bool valid();
+	bool showInEnvironment(const char *environment);
+	int run(const char *args);
+
+    private:
+	struct XDGEntry {
+	    char *key;
+	    char *locale;
+	    char *value;
+	    struct XDGEntry *next;
+	};
+
+	enum XDGEntryValueType {
+	    XDGString,
+	    XDGStrings,
+	    XDGLocalestring,
+	    XDGBoolean,
+	    XDGNumeric
+	};
+
+	bool valid_;
+	char *fileName_;
+	char *fileContent_;
+	struct XDGEntry *entries_;
+
+	bool processEntry(char *line);
+	bool storeEntry(struct XDGEntry *entry);
+	bool getEntry(const char *key, const char *locale, const char **value);
+	void validateEntries();
+
+	bool checkValueType(const char *value, XDGEntryValueType type);
+
+
+	// XXX: move all below this line to .cpp file?
+
+	enum XDGTypeMask {
+	    Application = 1,
+	    Link        = 2,
+	    Directory   = 4,
+	    // Old KDE stuff:
+	    ServiceType = 8,
+	    Service     = 16,
+	    FSDevice    = 32,
+	    // Deprecated:
+	    MimeType    = 64,
+	    All         = 127
+	};
+
+	typedef struct {
+	    const char *name;
+	    XDGEntryValueType valueType;
+	    bool required;
+	    XDGTypeMask type;
+	    bool deprecated;
+	    bool (*validValueFunc)(const char *);
+	} XDGEntryDescription;
+
+	static XDGEntryDescription registeredEntries[];
+
+	static bool validExec(const char *execString);
+
+	static int runCommand(const char *commandString);
+};
+
+#endif
diff -urN goplay-0.4/libxdgutils/XDGMenu.cpp goplay-0.4-pere//libxdgutils/XDGMenu.cpp
--- goplay-0.4/libxdgutils/XDGMenu.cpp	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGMenu.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+
+#include "XDGMenu.h"
+
+#include "XDGUtils.h"
+
+static const char *mainCategories[] = {
+    "AudioVideo",
+    "Audio",
+    "Video",
+    "Development",
+    "Education",
+    "Game",
+    "Graphics",
+    "Network",
+    "Office",
+    "Settings",
+    "System",
+    "Utility",
+    NULL
+};
+
+static const char *additionalCategories[] = {
+    "Building",
+    "Debugger",
+    "IDE",
+    "GUIDesigner",
+    "Profiling",
+    "RevisionControl",
+    "Translation",
+    "Calendar",
+    "ContactManagement",
+    "Database",
+    "Dictionary",
+    "Chart",
+    "Email",
+    "Finance",
+    "FlowChart",
+    "PDA",
+    "ProjectManagement",
+    "Presentation",
+    "Spreadsheet",
+    "WordProcessor",
+    "2DGraphics",
+    "VectorGraphics",
+    "RasterGraphics",
+    "3DGraphics",
+    "Scanning",
+    "OCR",
+    "Photography",
+    "Publishing",
+    "Viewer",
+    "TextTools",
+    "DesktopSettings",
+    "HardwareSettings",
+    "Printing",
+    "PackageManager",
+    "Dialup",
+    "InstantMessaging",
+    "Chat",
+    "IRCClient",
+    "FileTransfer",
+    "HamRadio",
+    "News",
+    "P2P",
+    "RemoteAccess",
+    "Telephony",
+    "TelephonyTools",
+    "VideoConference",
+    "WebBrowser",
+    "WebDevelopment",
+    "Midi",
+    "Mixer",
+    "Sequencer",
+    "Tuner",
+    "TV",
+    "AudioVideoEditing",
+    "Player",
+    "Recorder",
+    "DiscBurning",
+    "ActionGame",
+    "AdventureGame",
+    "ArcadeGame",
+    "BoardGame",
+    "BlocksGame",
+    "CardGame",
+    "KidsGame",
+    "LogicGame",
+    "RolePlaying",
+    "Simulation",
+    "SportsGame",
+    "StrategyGame",
+    "Art",
+    "Construction",
+    "Music",
+    "Languages",
+    "Science",
+    "ArtificialIntelligence",
+    "Astronomy",
+    "Biology",
+    "Chemistry",
+    "ComputerScience",
+    "DataVisualization",
+    "Economy",
+    "Electricity",
+    "Geography",
+    "Geology",
+    "Geoscience",
+    "History",
+    "ImageProcessing",
+    "Literature",
+    "Math",
+    "NumericalAnalysis",
+    "MedicalSoftware",
+    "Physics",
+    "Robotics",
+    "Sports",
+    "ParallelComputing",
+    "Amusement",
+    "Archiving",
+    "Compression",
+    "Electronics",
+    "Emulator",
+    "Engineering",
+    "FileTools",
+    "FileManager",
+    "TerminalEmulator",
+    "Filesystem",
+    "Monitor",
+    "Security",
+    "Accessibility",
+    "Calculator",
+    "Clock",
+    "TextEditor",
+    "Documentation",
+    "Core",
+    "KDE",
+    "GNOME",
+    "GTK",
+    "Qt",
+    "Motif",
+    "Java",
+    "ConsoleOnly",
+    NULL
+};
+
+static const char *reservedCategories[] = {
+    "Screensaver",
+    "TrayIcon",
+    "Applet",
+    "Shell",
+    NULL
+};
+
+static const char *deprecatedCategories[] = {
+    "Application",
+    "Applications",
+    NULL
+};
+
+static const char *environments[] = {
+    "GNOME",
+    "KDE",
+    "LXDE",
+    "ROX",
+    "XFCE",
+    "Old",
+    NULL
+};
+
+bool XDGMenu::validCategories(const char *categoriesString)
+{
+    char *s, *tokPtr, *tokStr;
+    bool mainFound = false;
+    int i;
+
+    // Allow empty categories for now
+    if (!categoriesString)
+	return true;
+
+    if (!XDGUtils::validSemicolonList(categoriesString))
+	return false;
+
+    s = strdup(categoriesString);
+
+    ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
+	if (!validCategory(tokStr)) {
+	    free(s);
+	    return false;
+	}
+
+	if (!mainFound) {
+	    for(i = 0; mainCategories[i] != NULL; i++) {
+		if (strcmp(tokStr, mainCategories[i]) == 0)
+		    mainFound = true;
+	    }
+	}
+    }
+
+    // "Audio" or "Video" entries require "AudioVideo"
+    char *s2, *tokPtr2, *tokStr2;
+    s2 = strdup(categoriesString);
+    ITERATE_ESCAPED_STRTOK_R(s, ":", &tokPtr, tokStr) {
+	if ((strcmp(tokStr, "Audio") == 0) || (strcmp(tokStr, "Video") == 0)) {
+	    ITERATE_ESCAPED_STRTOK_R(s2, ":", &tokPtr2, tokStr2) {
+		if (strcmp(tokStr2, "AudioVideo") == 0)
+		    goto done;
+	    }
+	    free(s2);
+	    free(s);
+	    std::cerr << "Audio or Video found, but no AudioVideo\n";
+	    return false;
+	}
+    }
+
+done:
+    if (!mainFound)
+	std::cerr << "Main category missing!\n";
+
+    free(s);
+    free(s2);
+    return mainFound;
+    // XXX: the spec suggests that some of the additional categories should only
+    // be used with specific main categories: we should check for this!
+    //
+    // XXX: reserved categories can only be present if we have a OnlyShowIn=
+    // entry in the desktop file!
+}
+
+
+bool XDGMenu::validCategory(const char *category)
+{
+    int i;
+
+    for(i = 0; mainCategories[i] != NULL; i++)
+	if (strcmp(category, mainCategories[i]) == 0)
+	    return true;
+
+    for(i = 0; additionalCategories[i] !=  NULL; i++)
+	if (strcmp(category, additionalCategories[i]) == 0)
+	    return true;
+
+    for(i = 0; reservedCategories[i] !=  NULL; i++)
+	if (strcmp(category, reservedCategories[i]) == 0) {
+	    std::cout << "Warning: reserved category \""
+		      << reservedCategories[i] << "\"\n";
+	    return true;
+	}
+    for(i = 0; deprecatedCategories[i] != NULL; i++)
+	if (strcmp(category, deprecatedCategories[i]) == 0) {
+	    std::cout << "Warning: deprecated category \""
+		      << deprecatedCategories[i] << "\"\n";
+	    return true;
+	}
+    if (category[0] == 'X' && category[1] == '-')
+	return true;
+
+    std::cerr << "Invalid category: " << category << "\n";
+    return false;
+}
+
+bool XDGMenu::validEnvironments(const char *environmentsString)
+{
+    char *s, *tokPtr, *tokStr;
+
+    if (!XDGUtils::validSemicolonList(environmentsString))
+	return false;
+
+    s = strdup(environmentsString);
+
+    ITERATE_ESCAPED_STRTOK_R(s, ";", &tokPtr, tokStr) {
+	if (!validEnvironment(tokStr)) {
+	    free(s);
+	    return false;
+	}
+    }
+
+    free(s);
+    return true;
+}
+
+bool XDGMenu::validEnvironment(const char *environment)
+{
+    int i;
+    for(i = 0; environments[i] != NULL; i++)
+	if (strcmp(environment, environments[i]) == 0)
+	    return true;
+
+    return false;
+}
diff -urN goplay-0.4/libxdgutils/XDGMenu.h goplay-0.4-pere//libxdgutils/XDGMenu.h
--- goplay-0.4/libxdgutils/XDGMenu.h	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGMenu.h	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef XDG_MENU_H
+#define XDG_MENU_H
+
+class XDGMenu
+{
+    public:
+	static bool validCategories(const char *categoriesString);
+	static bool validCategory(const char *category);
+
+	static bool validEnvironments(const char *environmentsString);
+	static bool validEnvironment(const char *environment);
+};
+
+#endif
diff -urN goplay-0.4/libxdgutils/XDGUtils.cpp goplay-0.4-pere//libxdgutils/XDGUtils.cpp
--- goplay-0.4/libxdgutils/XDGUtils.cpp	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGUtils.cpp	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#include <cstdlib>
+#include <cstdarg>
+#include <cstdio>
+#include <cstring>
+#include <iostream>
+
+extern "C" {
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+}
+
+#include "XDGUtils.h"
+
+XDGUtils::XDGLogLevel XDGUtils::logLevel_ = Quiet;
+
+bool XDGUtils::validSemicolonList(const char *list)
+{
+    size_t len;
+
+    // Last char must be unescaped ';'
+    len = strlen(list);
+    if (list[len-1] != ';' || list[len-2] == '\\')
+        return false;
+
+    return true;
+}
+
+char *XDGUtils::escapedStrTok_r(char *s, const char *delim,
+				char **ptrptr)
+{
+    char *checkedString;
+    int i;
+
+    if (s)
+	checkedString = s;
+    else
+	checkedString = *ptrptr;
+
+    for(i = 0; checkedString[i] != '\0'; i++) {
+	if (checkedString[i] == '\\')
+	    i++;
+	if (strchr(delim, checkedString[i])) {
+	    checkedString[i] = '\0';
+	    *ptrptr = &checkedString[i+1];
+	    return checkedString;
+	}
+    }
+    return NULL;
+}
+
+bool XDGUtils::findFileInPath(const char *fileName,
+			      std::string& fullPath)
+{
+    char *path = strdup(getenv("PATH"));
+    char *tokStr, *tokPtr;
+
+    ITERATE_STRTOK_R(path, ":", &tokPtr, tokStr) {
+	std::string tmp(std::string(tokStr) + '/' + fileName);
+	if (fileExists(tmp.c_str())) {
+	    fullPath = tmp;
+	    free(path);
+	    return true;
+	}
+    }
+    free(path);
+    return false;
+}
+
+bool XDGUtils::fileExists(const char *filePath)
+{
+    struct stat statBuffer;
+    return (stat(filePath, &statBuffer) == 0);
+}
+
+void XDGUtils::setLogLevel(XDGLogLevel logLevel)
+{
+    logLevel_ = logLevel;
+}
+
+void XDGUtils::log(XDGLogLevel logLevel, const char *fmt, ...)
+{
+    va_list argp;
+    va_start(argp, fmt);
+
+    if (logLevel_ >= logLevel)
+        vprintf(fmt, argp);
+    va_end(argp);
+}
diff -urN goplay-0.4/libxdgutils/XDGUtils.h goplay-0.4-pere//libxdgutils/XDGUtils.h
--- goplay-0.4/libxdgutils/XDGUtils.h	1970-01-01 01:00:00.000000000 +0100
+++ goplay-0.4-pere//libxdgutils/XDGUtils.h	2012-02-08 22:21:51.000000000 +0100
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011 Paulo Zanoni
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef XDG_UTILS_H
+#define XDG_UTILS_H
+
+#include <string>
+
+#define ITERATE_STRTOK_R(s, delim, ptrptr, tokStr) \
+	for(tokStr = strtok_r(s, delim, ptrptr); \
+	    tokStr != NULL; \
+	    tokStr = strtok_r(NULL, delim, ptrptr))
+
+#define ITERATE_ESCAPED_STRTOK_R(s, delim, ptrptr, tokStr) \
+	for(tokStr = XDGUtils::escapedStrTok_r(s, delim, ptrptr); \
+	    tokStr != NULL; \
+	    tokStr = XDGUtils::escapedStrTok_r(NULL, delim, ptrptr))
+
+class XDGUtils
+{
+    public:
+
+	static bool validSemicolonList(const char *list);
+
+	static char *escapedStrTok_r(char *s, const char *delim,
+				     char **ptrptr);
+
+	static bool findFileInPath(const char *fileName,
+				   std::string& fullPath);
+
+	static bool fileExists(const char *filePath);
+
+	enum XDGLogLevel {
+	    Quiet   = 0,
+	    Verbose = 1,
+	    Debug   = 2
+	};
+
+	static void setLogLevel(XDGLogLevel logLevel);
+	static void log(XDGLogLevel logLevel, const char *fmt, ...);
+
+    private:
+	static XDGLogLevel logLevel_;
+
+};
+
+#endif


More information about the Pkg-games-devel mailing list