summaryrefslogtreecommitdiff
path: root/libcaja-private
diff options
context:
space:
mode:
Diffstat (limited to 'libcaja-private')
-rw-r--r--libcaja-private/caja-query.c51
-rw-r--r--libcaja-private/caja-query.h4
-rw-r--r--libcaja-private/caja-search-engine-simple.c159
3 files changed, 205 insertions, 9 deletions
diff --git a/libcaja-private/caja-query.c b/libcaja-private/caja-query.c
index e0359a04..a5e783e4 100644
--- a/libcaja-private/caja-query.c
+++ b/libcaja-private/caja-query.c
@@ -35,6 +35,7 @@ struct CajaQueryDetails
char *text;
char *location_uri;
GList *mime_types;
+ GList *tags;
};
static void caja_query_class_init (CajaQueryClass *class);
@@ -129,6 +130,29 @@ caja_query_add_mime_type (CajaQuery *query, const char *mime_type)
g_strdup (mime_type));
}
+GList *
+caja_query_get_tags (CajaQuery *query)
+{
+ return eel_g_str_list_copy (query->details->tags);
+}
+
+void
+caja_query_set_tags (CajaQuery *query, GList *tags)
+{
+ g_list_free_full (query->details->tags, g_free);
+ query->details->tags = eel_g_str_list_copy (tags);
+}
+
+void
+caja_query_add_tag (CajaQuery *query, const char *tag)
+{
+ gchar *normalized = g_utf8_normalize (tag, -1, G_NORMALIZE_NFD);
+ gchar *lower_case = g_utf8_strdown (normalized, -1);
+
+ g_free (normalized);
+ query->details->tags = g_list_append (query->details->tags, lower_case);
+}
+
char *
caja_query_to_readable_string (CajaQuery *query)
{
@@ -196,6 +220,8 @@ typedef struct
gboolean in_location;
gboolean in_mimetypes;
gboolean in_mimetype;
+ gboolean in_tags;
+ gboolean in_tag;
} ParserInfo;
static void
@@ -218,6 +244,10 @@ start_element_cb (GMarkupParseContext *ctx,
info->in_mimetypes = TRUE;
else if (strcmp (element_name, "mimetype") == 0)
info->in_mimetype = TRUE;
+ else if (strcmp (element_name, "tags") == 0)
+ info->in_tags = TRUE;
+ else if (strcmp (element_name, "tag") == 0)
+ info->in_tag = TRUE;
}
static void
@@ -238,6 +268,10 @@ end_element_cb (GMarkupParseContext *ctx,
info->in_mimetypes = FALSE;
else if (strcmp (element_name, "mimetype") == 0)
info->in_mimetype = FALSE;
+ else if (strcmp (element_name, "tags") == 0)
+ info->in_tags = FALSE;
+ else if (strcmp (element_name, "tag") == 0)
+ info->in_tag = FALSE;
}
static void
@@ -268,6 +302,10 @@ text_cb (GMarkupParseContext *ctx,
{
caja_query_add_mime_type (info->query, t);
}
+ else if (info->in_tags && info->in_tag)
+ {
+ caja_query_add_tag (info->query, t);
+ }
g_free (t);
@@ -339,6 +377,7 @@ caja_query_to_xml (CajaQuery *query)
char *text;
char *uri;
char *mimetype;
+ char *tag;
GList *l;
xml = g_string_new ("");
@@ -369,6 +408,18 @@ caja_query_to_xml (CajaQuery *query)
g_string_append (xml, " </mimetypes>\n");
}
+ if (query->details->tags)
+ {
+ g_string_append (xml, " <tags>\n");
+ for (l = query->details->tags; l != NULL; l = l->next)
+ {
+ tag = g_markup_escape_text (l->data, -1);
+ g_string_append_printf (xml, " <tag>%s</tag>\n", tag);
+ g_free (tag);
+ }
+ g_string_append (xml, " </tags>\n");
+ }
+
g_string_append (xml, "</query>\n");
return g_string_free (xml, FALSE);
diff --git a/libcaja-private/caja-query.h b/libcaja-private/caja-query.h
index a13c930e..8e0f1047 100644
--- a/libcaja-private/caja-query.h
+++ b/libcaja-private/caja-query.h
@@ -57,6 +57,10 @@ void caja_query_set_text (CajaQuery *query, const char *text
char * caja_query_get_location (CajaQuery *query);
void caja_query_set_location (CajaQuery *query, const char *uri);
+GList * caja_query_get_tags (CajaQuery *query);
+void caja_query_set_tags (CajaQuery *query, GList *tags);
+void caja_query_add_tag (CajaQuery *query, const char *tag);
+
GList * caja_query_get_mime_types (CajaQuery *query);
void caja_query_set_mime_types (CajaQuery *query, GList *mime_types);
void caja_query_add_mime_type (CajaQuery *query, const char *mime_type);
diff --git a/libcaja-private/caja-search-engine-simple.c b/libcaja-private/caja-search-engine-simple.c
index e4078b36..016b03e6 100644
--- a/libcaja-private/caja-search-engine-simple.c
+++ b/libcaja-private/caja-search-engine-simple.c
@@ -38,6 +38,7 @@ typedef struct
GCancellable *cancellable;
GList *mime_types;
+ GList *tags;
char **words;
GList *found_list;
@@ -121,6 +122,7 @@ search_thread_data_new (CajaSearchEngineSimple *engine,
g_free (lower);
g_free (normalized);
+ data->tags = caja_query_get_tags (query);
data->mime_types = caja_query_get_mime_types (query);
data->cancellable = g_cancellable_new ();
@@ -137,6 +139,7 @@ search_thread_data_free (SearchThreadData *data)
g_hash_table_destroy (data->visited);
g_object_unref (data->cancellable);
g_strfreev (data->words);
+ g_list_free_full (data->tags, g_free);
g_list_free_full (data->mime_types, g_free);
g_list_free_full (data->uri_hits, g_free);
g_free (data);
@@ -203,6 +206,8 @@ send_batch (SearchThreadData *data)
data->uri_hits = NULL;
}
+#define G_FILE_ATTRIBUTE_XATTR_XDG_TAGS "xattr::xdg.tags"
+
#define STD_ATTRIBUTES \
G_FILE_ATTRIBUTE_STANDARD_NAME "," \
G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME "," \
@@ -210,13 +215,131 @@ send_batch (SearchThreadData *data)
G_FILE_ATTRIBUTE_STANDARD_TYPE "," \
G_FILE_ATTRIBUTE_ID_FILE
+
+/* Stolen code
+ * file: glocalfileinfo.c
+ * function: hex_unescape_string
+ * GIO - GLib Input, Output and Streaming Library
+ */
+static char *
+hex_unescape_string (const char *str,
+ int *out_len,
+ gboolean *free_return)
+{
+ int i;
+ char *unescaped_str, *p;
+ unsigned char c;
+ int len;
+
+ len = strlen (str);
+
+ if (strchr (str, '\\') == NULL)
+ {
+ if (out_len)
+ *out_len = len;
+ *free_return = FALSE;
+ return (char *)str;
+ }
+
+ unescaped_str = g_malloc (len + 1);
+
+ p = unescaped_str;
+ for (i = 0; i < len; i++)
+ {
+ if (str[i] == '\\' &&
+ str[i+1] == 'x' &&
+ len - i >= 4)
+ {
+ c =
+ (g_ascii_xdigit_value (str[i+2]) << 4) |
+ g_ascii_xdigit_value (str[i+3]);
+ *p++ = c;
+ i += 3;
+ }
+ else
+ *p++ = str[i];
+ }
+ *p++ = 0;
+
+ if (out_len)
+ *out_len = p - unescaped_str;
+ *free_return = TRUE;
+ return unescaped_str;
+}
+/* End of stolen code */
+
+static inline gchar **
+get_tags_from_info (GFileInfo *info)
+{
+ char **result;
+ const gchar *escaped_tags_string
+ = g_file_info_get_attribute_string (info, G_FILE_ATTRIBUTE_XATTR_XDG_TAGS);
+
+ gboolean new_created;
+ gchar *tags_string = hex_unescape_string (escaped_tags_string,
+ NULL,
+ &new_created);
+
+ gchar *normalized = g_utf8_normalize (tags_string, -1, G_NORMALIZE_NFD);
+
+ if (new_created)
+ g_free (tags_string);
+
+ gchar *lower_case = g_utf8_strdown (normalized, -1);
+ g_free (normalized);
+
+ result = g_strsplit (lower_case, ",", -1);
+ g_free (lower_case);
+
+ return result;
+}
+
+static inline gboolean
+file_has_all_tags (GFileInfo *info, GList *tags)
+{
+ if (g_list_length (tags) == 0) {
+ return TRUE;
+ }
+
+ if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_XATTR_XDG_TAGS))
+ {
+ return FALSE;
+ }
+
+ char **file_tags = get_tags_from_info (info);
+
+ guint file_tags_len = g_strv_length (file_tags);
+ if (file_tags_len < g_list_length (tags)) {
+ g_strfreev (file_tags);
+ return FALSE;
+ }
+
+ for (GList *l = tags; l != NULL; l = l->next) {
+ gboolean found = FALSE;
+ for (int i = 0; i < file_tags_len; ++i) {
+ if (g_strcmp0 (l->data, file_tags[i]) == 0) {
+ found = TRUE;
+ break;
+ }
+ }
+
+ if (found == FALSE) {
+ g_strfreev (file_tags);
+ return FALSE;
+ }
+ }
+
+ g_strfreev (file_tags);
+ return TRUE;
+}
+
static void
visit_directory (GFile *dir, SearchThreadData *data)
{
GFileEnumerator *enumerator;
GFileInfo *info;
GFile *child;
- const char *mime_type, *display_name;
+ const char *tag, *mime_type, *display_name;
char *lower_name, *normalized;
gboolean hit;
int i;
@@ -224,14 +347,27 @@ visit_directory (GFile *dir, SearchThreadData *data)
const char *id;
gboolean visited;
- enumerator = g_file_enumerate_children (dir,
- data->mime_types != NULL ?
- STD_ATTRIBUTES ","
- G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE
- :
- STD_ATTRIBUTES
- ,
- 0, data->cancellable, NULL);
+ const char *attributes;
+ if (data->mime_types != NULL) {
+ if (data->tags != NULL) {
+ attributes = STD_ATTRIBUTES ","
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE ","
+ G_FILE_ATTRIBUTE_XATTR_XDG_TAGS;
+ } else {
+ attributes = STD_ATTRIBUTES ","
+ G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE;
+ }
+ } else {
+ if (data->tags != NULL) {
+ attributes = STD_ATTRIBUTES ","
+ G_FILE_ATTRIBUTE_XATTR_XDG_TAGS;
+ } else {
+ attributes = STD_ATTRIBUTES;
+ }
+ }
+
+ enumerator = g_file_enumerate_children (dir, attributes, 0,
+ data->cancellable, NULL);
if (enumerator == NULL)
{
@@ -281,6 +417,11 @@ visit_directory (GFile *dir, SearchThreadData *data)
}
}
+ if (hit && data->tags)
+ {
+ hit = file_has_all_tags (info, data->tags);
+ }
+
child = g_file_get_child (dir, g_file_info_get_name (info));
if (hit)