[RFC PATCH 3/5] osinfo_loader: introduce a compiled xpaths cache

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

 



Signed-off-by: Giuseppe Scrivano <gscrivan@xxxxxxxxxx>
---
 osinfo/osinfo_loader.c | 159 ++++++++++++++++++++++++++++++++++---------------
 1 file changed, 111 insertions(+), 48 deletions(-)

diff --git a/osinfo/osinfo_loader.c b/osinfo/osinfo_loader.c
index a767bb8..6b1d3e1 100644
--- a/osinfo/osinfo_loader.c
+++ b/osinfo/osinfo_loader.c
@@ -55,6 +55,7 @@ G_DEFINE_TYPE (OsinfoLoader, osinfo_loader, G_TYPE_OBJECT);
 struct _OsinfoLoaderPrivate
 {
     OsinfoDb *db;
+    GHashTable *xpath_cache;
 };
 
 struct _OsinfoEntityKey
@@ -70,6 +71,7 @@ osinfo_loader_finalize (GObject *object)
     OsinfoLoader *loader = OSINFO_LOADER (object);
 
     g_object_unref(loader->priv->db);
+    g_hash_table_destroy(loader->priv->xpath_cache);
 
     /* Chain up to the parent class */
     G_OBJECT_CLASS (osinfo_loader_parent_class)->finalize (object);
@@ -89,11 +91,21 @@ osinfo_loader_class_init (OsinfoLoaderClass *klass)
     g_type_class_add_private (klass, sizeof (OsinfoLoaderPrivate));
 }
 
+
+static void xpath_cache_value_free(gpointer values)
+{
+    xmlXPathFreeCompExpr(values);
+}
+
 static void
 osinfo_loader_init (OsinfoLoader *loader)
 {
     loader->priv = OSINFO_LOADER_GET_PRIVATE(loader);
     loader->priv->db = osinfo_db_new();
+    loader->priv->xpath_cache = g_hash_table_new_full(g_str_hash,
+                                                      g_str_equal,
+                                                      g_free,
+                                                      xpath_cache_value_free);
 }
 
 /** PUBLIC METHODS */
@@ -118,8 +130,20 @@ static gboolean error_is_set(GError **error)
     return ((error != NULL) && (*error != NULL));
 }
 
+static xmlXPathCompExprPtr osinfo_loader_get_comp_xpath(OsinfoLoader *loader,
+                                                        const char *xpath)
+{
+    xmlXPathCompExprPtr comp = g_hash_table_lookup(loader->priv->xpath_cache,
+                                                   xpath);
+    if (comp == NULL) {
+        comp = xmlXPathCompile(BAD_CAST xpath);
+        g_hash_table_insert(loader->priv->xpath_cache, g_strdup(xpath), comp);
+    }
+    return comp;
+}
+
 static int
-osinfo_loader_nodeset(const char *xpath,
+osinfo_loader_nodeset(xmlXPathCompExprPtr comp,
                       xmlXPathContextPtr ctxt,
                       xmlNodePtr **list,
                       GError **err)
@@ -129,19 +153,19 @@ osinfo_loader_nodeset(const char *xpath,
     int ret;
 
     g_return_val_if_fail(ctxt != NULL, -1);
-    g_return_val_if_fail(xpath != NULL, -1);
+    g_return_val_if_fail(comp != NULL, -1);
 
     if (list != NULL)
         *list = NULL;
 
     relnode = ctxt->node;
-    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
+    obj = xmlXPathCompiledEval(comp, ctxt);
     ctxt->node = relnode;
     if (obj == NULL)
         return(0);
     if (obj->type != XPATH_NODESET) {
         g_set_error(err, g_quark_from_static_string("libosinfo"), 0,
-                    _("Expected a nodeset in XPath query %s"), xpath);
+                    _("Expected a nodeset in XPath query"));
         xmlXPathFreeObject(obj);
         return (-1);
     }
@@ -161,7 +185,7 @@ osinfo_loader_nodeset(const char *xpath,
 }
 
 static gchar *
-osinfo_loader_string(const char *xpath,
+osinfo_loader_string(xmlXPathCompExprPtr comp,
                      xmlXPathContextPtr ctxt,
                      GError **err)
 {
@@ -169,11 +193,9 @@ osinfo_loader_string(const char *xpath,
     xmlNodePtr relnode;
     gchar *ret;
 
-    g_return_val_if_fail(ctxt != NULL, NULL);
-    g_return_val_if_fail(xpath != NULL, NULL);
-
     relnode = ctxt->node;
-    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
+    obj = xmlXPathCompiledEval(comp, ctxt);
+
     ctxt->node = relnode;
     if ((obj == NULL) || (obj->type != XPATH_STRING) ||
         (obj->stringval == NULL) || (obj->stringval[0] == 0)) {
@@ -187,7 +209,7 @@ osinfo_loader_string(const char *xpath,
 }
 
 static gboolean
-osinfo_loader_boolean(const char *xpath,
+osinfo_loader_boolean(xmlXPathCompExprPtr comp,
                       xmlXPathContextPtr ctxt,
                       GError **err)
 {
@@ -198,9 +220,9 @@ osinfo_loader_boolean(const char *xpath,
     gboolean ret = FALSE;
 
     g_return_val_if_fail(ctxt != NULL, FALSE);
-    g_return_val_if_fail(xpath != NULL, FALSE);
+    g_return_val_if_fail(comp != NULL, FALSE);
 
-    count = osinfo_loader_nodeset(xpath, ctxt, &nodes, err);
+    count = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
 
     if (count < 0) {
         return FALSE;
@@ -229,7 +251,7 @@ cleanup:
 }
 
 static gchar *
-osinfo_loader_doc(const char *xpath,
+osinfo_loader_doc(xmlXPathCompExprPtr comp,
                   xmlXPathContextPtr ctxt,
                   GError **err)
 {
@@ -239,10 +261,10 @@ osinfo_loader_doc(const char *xpath,
     xmlBufferPtr buf;
 
     g_return_val_if_fail(ctxt != NULL, NULL);
-    g_return_val_if_fail(xpath != NULL, NULL);
+    g_return_val_if_fail(comp != NULL, NULL);
 
     relnode = ctxt->node;
-    obj = xmlXPathEval(BAD_CAST xpath, ctxt);
+    obj = xmlXPathCompiledEval(comp, ctxt);
     ctxt->node = relnode;
     if ((obj == NULL) || (obj->type != XPATH_NODESET)) {
         xmlXPathFreeObject(obj);
@@ -283,6 +305,7 @@ static void osinfo_loader_entity(OsinfoLoader *loader,
         gboolean value_bool = FALSE;
         gchar *xpath = NULL;
         int j;
+        xmlXPathCompExprPtr comp;
 
         /* We are guaranteed to have at least the default "C" locale and we
          * want to ignore that, hence the NULL check on index 'j + 1'.
@@ -291,7 +314,8 @@ static void osinfo_loader_entity(OsinfoLoader *loader,
             for (j = 0; langs[j + 1] != NULL; j++) {
                 xpath = g_strdup_printf("string(./%s[lang('%s')])",
                                         keys[i].name, langs[j]);
-                value_str = osinfo_loader_string(xpath, ctxt, err);
+                comp = osinfo_loader_get_comp_xpath(loader, xpath);
+                value_str = osinfo_loader_string(comp, ctxt, err);
                 g_free(xpath);
                 xpath = NULL;
                 if (error_is_set(err))
@@ -304,20 +328,23 @@ static void osinfo_loader_entity(OsinfoLoader *loader,
 
         switch (keys[i].type) {
             case G_TYPE_STRING:
-                xpath = g_strdup_printf("string(./%s)", keys[i].name);
                 if (value_str == NULL) {
-                    value_str = osinfo_loader_string(xpath, ctxt, err);
+                    xpath = g_strdup_printf("string(./%s)", keys[i].name);
+                    comp = osinfo_loader_get_comp_xpath(loader, xpath);
+                    value_str = osinfo_loader_string(comp, ctxt, err);
+                    g_free(xpath);
                 }
                 break;
             case G_TYPE_BOOLEAN:
                 xpath = g_strdup_printf("./%s", keys[i].name);
-                value_bool = osinfo_loader_boolean(xpath, ctxt, err);
+                comp = osinfo_loader_get_comp_xpath(loader, xpath);
+                value_bool = osinfo_loader_boolean(comp, ctxt, err);
+                g_free(xpath);
                 break;
             default:
                 g_warn_if_reached();
                 break;
         }
-        g_free(xpath);
 
         switch (keys[i].type) {
             case G_TYPE_STRING:
@@ -339,7 +366,10 @@ static void osinfo_loader_entity(OsinfoLoader *loader,
 
     /* Then any site specific custom keys. x-... Can be repeated */
     xmlNodePtr *custom = NULL;
-    int ncustom = osinfo_loader_nodeset("./*[substring(name(),1,2)='x-']", ctxt, &custom, err);
+    xmlXPathCompExprPtr comp;
+    comp = osinfo_loader_get_comp_xpath(loader,
+                                        "./*[substring(name(),1,2)='x-']");
+    int ncustom = osinfo_loader_nodeset(comp, ctxt, &custom, err);
     if (error_is_set(err))
         return;
 
@@ -457,7 +487,8 @@ static void osinfo_loader_device_link(OsinfoLoader *loader,
                                       GError **err)
 {
     xmlNodePtr *related = NULL;
-    int nrelated = osinfo_loader_nodeset(xpath, ctxt, &related, err);
+    xmlXPathCompExprPtr comp = osinfo_loader_get_comp_xpath(loader, xpath);
+    int nrelated = osinfo_loader_nodeset(comp, ctxt, &related, err);
     int i;
     if (error_is_set(err))
         return;
@@ -505,7 +536,8 @@ static void osinfo_loader_product_relshp(OsinfoLoader *loader,
                                          GError **err)
 {
     xmlNodePtr *related = NULL;
-    int nrelated = osinfo_loader_nodeset(xpath, ctxt, &related, err);
+    xmlXPathCompExprPtr comp = osinfo_loader_get_comp_xpath(loader, xpath);
+    int nrelated = osinfo_loader_nodeset(comp, ctxt, &related, err);
     int i;
     if (error_is_set(err))
         return;
@@ -611,13 +643,15 @@ static void osinfo_loader_deployment(OsinfoLoader *loader,
                                      xmlNodePtr root,
                                      GError **err)
 {
+    xmlXPathCompExprPtr comp;
     gchar *id = (gchar *)xmlGetProp(root, BAD_CAST "id");
     if (!id) {
         OSINFO_ERROR(err, _("Missing deployment id property"));
         return;
     }
 
-    gchar *osid = osinfo_loader_string("string(./os/@id)", ctxt, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "string(./os/@id)");
+    gchar *osid = osinfo_loader_string(comp, ctxt, err);
     if (!osid && 0) {
         OSINFO_ERROR(err, _("Missing deployment os id property"));
         xmlFree(id);
@@ -626,7 +660,8 @@ static void osinfo_loader_deployment(OsinfoLoader *loader,
     OsinfoOs *os = osinfo_loader_get_os(loader, osid);
     g_free(osid);
 
-    gchar *platformid = osinfo_loader_string("string(./platform/@id)", ctxt, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "string(./platform/@id)");
+    gchar *platformid = osinfo_loader_string(comp, ctxt, err);
     if (!platformid) {
         OSINFO_ERROR(err, _("Missing deployment platform id property"));
         xmlFree(id);
@@ -672,7 +707,8 @@ static void osinfo_loader_datamap(OsinfoLoader *loader,
 
     OsinfoDatamap *map = osinfo_loader_get_datamap(loader, id);
 
-    nnodes = osinfo_loader_nodeset("./entry", ctxt, &nodes, err);
+    xmlXPathCompExprPtr comp = osinfo_loader_get_comp_xpath(loader, "./entry");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -702,7 +738,8 @@ static void osinfo_loader_install_config_params(OsinfoLoader *loader,
                                                 GError **err)
 {
     xmlNodePtr *nodes = NULL;
-    int nnodes = osinfo_loader_nodeset(xpath, ctxt, &nodes, err);
+    xmlXPathCompExprPtr comp = osinfo_loader_get_comp_xpath(loader, xpath);
+    int nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     int i;
     if (error_is_set(err))
         return;
@@ -786,6 +823,7 @@ static void osinfo_loader_install_script(OsinfoLoader *loader,
         return;
     }
 
+    xmlXPathCompExprPtr comp;
     OsinfoInstallScript *installScript = osinfo_loader_get_install_script(loader,
                                                                           id);
     xmlFree(id);
@@ -794,7 +832,8 @@ static void osinfo_loader_install_script(OsinfoLoader *loader,
     if (error_is_set(err))
         goto error;
 
-    value = osinfo_loader_doc("./template/*[1]", ctxt, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./template/*[1]");
+    value = osinfo_loader_doc(comp, ctxt, err);
     if (error_is_set(err))
         goto error;
     if (value)
@@ -803,7 +842,8 @@ static void osinfo_loader_install_script(OsinfoLoader *loader,
                                 value);
     g_free(value);
 
-    value = osinfo_loader_string("./template/@uri", ctxt, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./template/@uri");
+    value = osinfo_loader_string(comp, ctxt, err);
     if (error_is_set(err))
         goto error;
     if (value)
@@ -819,7 +859,8 @@ static void osinfo_loader_install_script(OsinfoLoader *loader,
                                         root,
                                         err);
 
-    nnodes = osinfo_loader_nodeset("./avatar-format", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./avatar-format");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto error;
 
@@ -837,7 +878,8 @@ static void osinfo_loader_install_script(OsinfoLoader *loader,
     }
     g_free(nodes);
 
-    nnodes = osinfo_loader_nodeset("./injection-method", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./injection-method");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto error;
 
@@ -872,6 +914,7 @@ static OsinfoMedia *osinfo_loader_media (OsinfoLoader *loader,
 {
     xmlNodePtr *nodes = NULL;
     guint i;
+    xmlXPathCompExprPtr comp;
 
     gchar *arch = (gchar *)xmlGetProp(root, BAD_CAST "arch");
     xmlChar *live = xmlGetProp(root, BAD_CAST OSINFO_MEDIA_PROP_LIVE);
@@ -910,7 +953,8 @@ static OsinfoMedia *osinfo_loader_media (OsinfoLoader *loader,
         xmlFree(installer_reboots);
     }
 
-    gint nnodes = osinfo_loader_nodeset("./variant", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./variant");
+    gint nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err)) {
         g_object_unref(media);
         return NULL;
@@ -925,7 +969,8 @@ static OsinfoMedia *osinfo_loader_media (OsinfoLoader *loader,
     }
     g_free(nodes);
 
-    nnodes = osinfo_loader_nodeset("./iso/*", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./iso/*");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err)) {
         g_object_unref(media);
         return NULL;
@@ -986,6 +1031,7 @@ static OsinfoTree *osinfo_loader_tree (OsinfoLoader *loader,
 {
     xmlNodePtr *nodes = NULL;
     guint i;
+    xmlXPathCompExprPtr comp;
 
     gchar *arch = (gchar *)xmlGetProp(root, BAD_CAST "arch");
     const OsinfoEntityKey keys[] = {
@@ -1001,7 +1047,8 @@ static OsinfoTree *osinfo_loader_tree (OsinfoLoader *loader,
 
     osinfo_loader_entity(loader, OSINFO_ENTITY(tree), keys, ctxt, root, err);
 
-    gint nnodes = osinfo_loader_nodeset("./treeinfo/*", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./treeinfo/*");
+    gint nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err)) {
         g_object_unref(G_OBJECT(tree));
         return NULL;
@@ -1071,7 +1118,8 @@ static OsinfoResources *osinfo_loader_resources(OsinfoLoader *loader,
 
     gchar *arch = (gchar *)xmlGetProp(root, BAD_CAST "arch");
     gchar *node_path = g_strjoin("/", ".", name, "*", NULL);
-    gint nnodes = osinfo_loader_nodeset(node_path, ctxt, &nodes, err);
+    xmlXPathCompExprPtr comp = osinfo_loader_get_comp_xpath(loader, node_path);
+    gint nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     g_free(node_path);
     if (error_is_set(err) || nnodes < 1)
         goto EXIT;
@@ -1175,7 +1223,8 @@ static OsinfoDeviceDriver *osinfo_loader_driver(OsinfoLoader *loader,
         xmlFree(is_signed);
     }
 
-    gint nnodes = osinfo_loader_nodeset("./*", ctxt, &nodes, err);
+    xmlXPathCompExprPtr comp = osinfo_loader_get_comp_xpath(loader, "./*");
+    gint nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err)) {
         g_object_unref(G_OBJECT(driver));
         return NULL;
@@ -1214,6 +1263,7 @@ static void osinfo_loader_os(OsinfoLoader *loader,
     xmlNodePtr *nodes = NULL;
     guint i;
     int nnodes;
+    xmlXPathCompExprPtr comp;
 
     gchar *id = (gchar *)xmlGetProp(root, BAD_CAST "id");
     const OsinfoEntityKey keys[] = {
@@ -1243,7 +1293,8 @@ static void osinfo_loader_os(OsinfoLoader *loader,
     if (error_is_set(err))
         goto cleanup;
 
-    nnodes = osinfo_loader_nodeset("./media", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./media");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1263,7 +1314,8 @@ static void osinfo_loader_os(OsinfoLoader *loader,
 
     g_free(nodes);
 
-    nnodes = osinfo_loader_nodeset("./tree", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./tree");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1283,7 +1335,8 @@ static void osinfo_loader_os(OsinfoLoader *loader,
 
     g_free(nodes);
 
-    nnodes = osinfo_loader_nodeset("./variant", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./variant");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1304,7 +1357,8 @@ static void osinfo_loader_os(OsinfoLoader *loader,
 
     g_free(nodes);
 
-    nnodes = osinfo_loader_nodeset("./resources", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./resources");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1326,7 +1380,8 @@ static void osinfo_loader_os(OsinfoLoader *loader,
     g_free(nodes);
 
 
-    nnodes = osinfo_loader_nodeset("./installer/script", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./installer/script");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1345,7 +1400,8 @@ static void osinfo_loader_os(OsinfoLoader *loader,
 
     g_free(nodes);
 
-    nnodes = osinfo_loader_nodeset("./driver", ctxt, &nodes, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./driver");
+    nnodes = osinfo_loader_nodeset(comp, ctxt, &nodes, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1406,13 +1462,15 @@ static void osinfo_loader_root(OsinfoLoader *loader,
     int nplatform;
     int ninstallScript;
     int ndataMaps;
+    xmlXPathCompExprPtr comp;
 
     if (!xmlStrEqual(root->name, BAD_CAST "libosinfo")) {
         OSINFO_ERROR(err, _("Incorrect root element"));
         return;
     }
 
-    ndevice = osinfo_loader_nodeset("./device", ctxt, &devices, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./device");
+    ndevice = osinfo_loader_nodeset(comp, ctxt, &devices, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1425,7 +1483,8 @@ static void osinfo_loader_root(OsinfoLoader *loader,
             goto cleanup;
     }
 
-    nplatform = osinfo_loader_nodeset("./platform", ctxt, &platforms, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./platform");
+    nplatform = osinfo_loader_nodeset(comp, ctxt, &platforms, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1438,7 +1497,8 @@ static void osinfo_loader_root(OsinfoLoader *loader,
             goto cleanup;
     }
 
-    nos = osinfo_loader_nodeset("./os", ctxt, &oss, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./os");
+    nos = osinfo_loader_nodeset(comp, ctxt, &oss, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1451,7 +1511,8 @@ static void osinfo_loader_root(OsinfoLoader *loader,
             goto cleanup;
     }
 
-    ndeployment = osinfo_loader_nodeset("./deployment", ctxt, &deployments, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./deployment");
+    ndeployment = osinfo_loader_nodeset(comp, ctxt, &deployments, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1464,7 +1525,8 @@ static void osinfo_loader_root(OsinfoLoader *loader,
             goto cleanup;
     }
 
-    ninstallScript = osinfo_loader_nodeset("./install-script", ctxt, &installScripts, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./install-script");
+    ninstallScript = osinfo_loader_nodeset(comp, ctxt, &installScripts, err);
     if (error_is_set(err))
         goto cleanup;
 
@@ -1477,7 +1539,8 @@ static void osinfo_loader_root(OsinfoLoader *loader,
             goto cleanup;
     }
 
-    ndataMaps = osinfo_loader_nodeset("./datamap", ctxt, &dataMaps, err);
+    comp = osinfo_loader_get_comp_xpath(loader, "./datamap");
+    ndataMaps = osinfo_loader_nodeset(comp, ctxt, &dataMaps, err);
     if (error_is_set(err))
         goto cleanup;
 
-- 
1.9.3

_______________________________________________
Libosinfo mailing list
Libosinfo@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/libosinfo




[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Fedora Users]     [Fedora Maintainers]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]

  Powered by Linux