1 /*
2 * Copyright (C) 2009, Nokia <ivan.frade@nokia.com>
3 * Copyright (C) 2014, Lanedo <martyn@lanedo.com>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #include "config.h"
22
23 #include <stdlib.h>
24 #include <time.h>
25 #include <locale.h>
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gio/gio.h>
30
31 #include <libtracker-sparql/tracker-sparql.h>
32
33 #include "tracker-tag.h"
34
35 #define TAG_OPTIONS_ENABLED() \
36 (resources || \
37 add_tag || \
38 remove_tag || \
39 list)
40
41 static gint limit = 512;
42 static gint offset;
43 static gchar **resources;
44 static gboolean and_operator;
45 static gchar *add_tag;
46 static gchar *remove_tag;
47 static gchar *description;
48 static gboolean *list;
49 static gboolean show_resources;
50
51 static GOptionEntry entries[] = {
52 { "list", 't', 0, G_OPTION_ARG_NONE, &list,
53 N_("List all tags (using FILTER if specified; FILTER always uses logical OR)"),
54 N_("FILTER"),
55 },
56 { "show-files", 's', 0, G_OPTION_ARG_NONE, &show_resources,
57 N_("Show files associated with each tag (this is only used with --list)"),
58 NULL
59 },
60 { "add", 'a', 0, G_OPTION_ARG_STRING, &add_tag,
61 N_("Add a tag (if FILEs are omitted, TAG is not associated with any files)"),
62 N_("TAG")
63 },
64 { "delete", 'd', 0, G_OPTION_ARG_STRING, &remove_tag,
65 N_("Delete a tag (if FILEs are omitted, TAG is removed for all files)"),
66 N_("TAG")
67 },
68 { "description", 'e', 0, G_OPTION_ARG_STRING, &description,
69 N_("Description for a tag (this is only used with --add)"),
70 N_("STRING")
71 },
72 { "limit", 'l', 0, G_OPTION_ARG_INT, &limit,
73 N_("Limit the number of results shown"),
74 "512"
75 },
76 { "offset", 'o', 0, G_OPTION_ARG_INT, &offset,
77 N_("Offset the results"),
78 "0"
79 },
80 { "and-operator", 'n', 0, G_OPTION_ARG_NONE, &and_operator,
81 N_("Use AND for search terms instead of OR (the default)"),
82 NULL
83 },
84 { G_OPTION_REMAINING, 0, 0,
85 G_OPTION_ARG_FILENAME_ARRAY, &resources,
86 N_("FILE…"),
87 N_("FILE [FILE…]")},
88 { NULL }
89 };
90
91 static void
show_limit_warning(void)92 show_limit_warning (void)
93 {
94 /* Display '...' so the user thinks there is
95 * more items.
96 */
97 g_print (" ...\n");
98
99 /* Display warning so the user knows this is
100 * not the WHOLE data set.
101 */
102 g_printerr ("\n%s\n",
103 _("NOTE: Limit was reached, there are more items in the database not listed here"));
104 }
105
106 static gchar *
get_escaped_sparql_string(const gchar * str)107 get_escaped_sparql_string (const gchar *str)
108 {
109 GString *sparql;
110
111 sparql = g_string_new ("");
112 g_string_append_c (sparql, '"');
113
114 while (*str != '\0') {
115 gsize len = strcspn (str, "\t\n\r\"\\");
116 g_string_append_len (sparql, str, len);
117 str += len;
118 switch (*str) {
119 case '\t':
120 g_string_append (sparql, "\\t");
121 break;
122 case '\n':
123 g_string_append (sparql, "\\n");
124 break;
125 case '\r':
126 g_string_append (sparql, "\\r");
127 break;
128 case '"':
129 g_string_append (sparql, "\\\"");
130 break;
131 case '\\':
132 g_string_append (sparql, "\\\\");
133 break;
134 default:
135 continue;
136 }
137 str++;
138 }
139
140 g_string_append_c (sparql, '"');
141
142 return g_string_free (sparql, FALSE);
143 }
144
145 static gchar *
get_filter_string(GStrv resources,const gchar * subject,gboolean resources_are_urns,const gchar * tag)146 get_filter_string (GStrv resources,
147 const gchar *subject,
148 gboolean resources_are_urns,
149 const gchar *tag)
150 {
151 GString *filter;
152 gint i, len;
153
154 if (!resources) {
155 return NULL;
156 }
157
158 len = g_strv_length (resources);
159
160 if (len < 1) {
161 return NULL;
162 }
163
164 filter = g_string_new ("");
165
166 g_string_append_printf (filter, "FILTER (");
167
168 if (tag) {
169 g_string_append (filter, "(");
170 }
171
172 for (i = 0; i < len; i++) {
173 g_string_append_printf (filter, "%s = %s%s%s",
174 subject,
175 resources_are_urns ? "<" : "\"",
176 resources[i],
177 resources_are_urns ? ">" : "\"");
178
179 if (i < len - 1) {
180 g_string_append (filter, " || ");
181 }
182 }
183
184 if (tag) {
185 g_string_append_printf (filter, ") && ?t = <%s>", tag);
186 }
187
188 g_string_append (filter, ")");
189
190 return g_string_free (filter, FALSE);
191 }
192
193 static GStrv
get_uris(GStrv resources)194 get_uris (GStrv resources)
195 {
196 GStrv uris;
197 gint len, i;
198
199 if (!resources) {
200 return NULL;
201 }
202
203 len = g_strv_length (resources);
204
205 if (len < 1) {
206 return NULL;
207 }
208
209 uris = g_new0 (gchar *, len + 1);
210
211 for (i = 0; resources[i]; i++) {
212 GFile *file;
213
214 file = g_file_new_for_commandline_arg (resources[i]);
215 uris[i] = g_file_get_uri (file);
216 g_object_unref (file);
217 }
218
219 return uris;
220 }
221
222 static TrackerSparqlCursor *
get_file_urns(TrackerSparqlConnection * connection,GStrv uris,const gchar * tag)223 get_file_urns (TrackerSparqlConnection *connection,
224 GStrv uris,
225 const gchar *tag)
226 {
227 TrackerSparqlCursor *cursor;
228 gchar *query, *filter;
229 GError *error = NULL;
230
231 filter = get_filter_string (uris, "?f", FALSE, tag);
232 query = g_strdup_printf ("SELECT ?urn ?f "
233 "WHERE { "
234 " ?urn "
235 " %s "
236 " nie:url ?f . "
237 " %s "
238 "}",
239 tag ? "nao:hasTag ?t ; " : "",
240 filter ? filter : "");
241
242 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
243
244 g_free (query);
245 g_free (filter);
246
247 if (error) {
248 g_print (" %s, %s\n",
249 _("Could not get file URNs"),
250 error->message);
251 g_error_free (error);
252 return NULL;
253 }
254
255 return cursor;
256 }
257
258 static GStrv
result_to_strv(TrackerSparqlCursor * cursor,gint n_col)259 result_to_strv (TrackerSparqlCursor *cursor,
260 gint n_col)
261 {
262 GStrv strv;
263 gint count, i;
264
265 if (!cursor) {
266 return NULL;
267 }
268
269 i = 0;
270 count = 0;
271
272 /* Really no other option here, but we iterate the cursor
273 * first to get the length.
274 */
275 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
276 count++;
277 }
278
279 strv = g_new0 (gchar *, count + 1);
280
281 tracker_sparql_cursor_rewind (cursor);
282
283 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
284 const gchar *str;
285
286 str = tracker_sparql_cursor_get_string (cursor, n_col, NULL);
287 strv[i++] = g_strdup (str);
288 }
289
290 return strv;
291 }
292
293 static void
get_all_tags_show_tag_id(TrackerSparqlConnection * connection,const gchar * id)294 get_all_tags_show_tag_id (TrackerSparqlConnection *connection,
295 const gchar *id)
296 {
297 TrackerSparqlCursor *cursor;
298 GError *error = NULL;
299 gchar *query;
300
301 /* Get resources associated */
302 query = g_strdup_printf ("SELECT ?uri WHERE {"
303 " ?urn a rdfs:Resource; "
304 " nie:url ?uri ; "
305 " nao:hasTag \"%s\" . "
306 "}",
307 id);
308
309 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
310 g_free (query);
311
312 if (error) {
313 g_printerr (" %s, %s\n",
314 _("Could not get files related to tag"),
315 error->message);
316 g_error_free (error);
317 return;
318 }
319
320 if (!cursor) {
321 /* To translators: This is to say there are no
322 * tags found with a particular unique ID. */
323 g_print (" %s\n", _("None"));
324 return;
325 }
326
327
328 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
329 g_print (" %s\n", tracker_sparql_cursor_get_string (cursor, 0, NULL));
330 }
331
332 g_object_unref (cursor);
333 }
334
335 static inline gchar *
get_filter_in_for_strv(GStrv resources,const gchar * subject)336 get_filter_in_for_strv (GStrv resources,
337 const gchar *subject)
338 {
339 gchar *filter, *filter_in;
340
341 /* e.g. '?label IN ("foo", "bar")' */
342 filter_in = g_strjoinv ("\",\"", resources);
343 filter = g_strdup_printf ("FILTER (%s IN (\"%s\"))", subject, filter_in);
344 g_free (filter_in);
345
346 return filter;
347 }
348
349 static gboolean
get_all_resources_with_tags(TrackerSparqlConnection * connection,GStrv tags,gint search_offset,gint search_limit)350 get_all_resources_with_tags (TrackerSparqlConnection *connection,
351 GStrv tags,
352 gint search_offset,
353 gint search_limit)
354 {
355 TrackerSparqlCursor *cursor;
356 GError *error = NULL;
357 GStrv tag_urns, p;
358 GString *s;
359 gchar *filter, *query;
360
361 if (!tags) {
362 return FALSE;
363 }
364
365 /* First, get matching tags */
366 filter = get_filter_in_for_strv (tags, "?label");
367 query = g_strdup_printf ("SELECT ?t "
368 "WHERE { "
369 " ?t a nao:Tag ;"
370 " nao:prefLabel ?label ."
371 " %s"
372 "}",
373 filter);
374 g_free (filter);
375
376 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
377 g_free (query);
378
379 if (error) {
380 g_printerr ("%s, %s\n",
381 _("Could not get all tags in the database"),
382 error->message);
383 g_error_free (error);
384
385 return FALSE;
386 }
387
388 tag_urns = result_to_strv (cursor, 0);
389 if (!tag_urns) {
390 g_print ("%s\n",
391 _("No files have been tagged"));
392
393 if (cursor) {
394 g_object_unref (cursor);
395 }
396
397 return TRUE;
398 }
399
400 s = g_string_new ("");
401
402 for (p = tag_urns; p && *p; p++) {
403 g_string_append_printf (s, "; nao:hasTag <%s>", *p);
404 }
405
406 s = g_string_append (s, " .");
407 filter = g_string_free (s, FALSE);
408 g_strfreev (tag_urns);
409
410 query = g_strdup_printf ("SELECT DISTINCT nie:url(?r) "
411 "WHERE {"
412 " ?r a rdfs:Resource %s"
413 "} "
414 "OFFSET %d "
415 "LIMIT %d",
416 filter,
417 search_offset,
418 search_limit);
419 g_free (filter);
420
421 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
422 g_free (query);
423
424 if (error) {
425 g_printerr ("%s, %s\n",
426 _("Could not get files for matching tags"),
427 error->message);
428 g_error_free (error);
429
430 return FALSE;
431 }
432
433 if (!cursor) {
434 g_print ("%s\n",
435 _("No files were found matching ALL of those tags"));
436 } else {
437 gint count = 0;
438
439 g_print ("%s:\n", _("Files"));
440
441 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
442 g_print (" %s\n",
443 tracker_sparql_cursor_get_string (cursor, 0, NULL));
444 count++;
445 }
446
447 if (count == 0) {
448 /* To translators: This is to say there are no
449 * files found associated with multiple tags, e.g.:
450 *
451 * Files:
452 * None
453 *
454 */
455 g_print (" %s\n", _("None"));
456 }
457
458 g_print ("\n");
459
460 if (count >= search_limit) {
461 show_limit_warning ();
462 }
463
464 g_object_unref (cursor);
465 }
466
467 return TRUE;
468 }
469
470
471 static gboolean
get_all_tags(TrackerSparqlConnection * connection,GStrv resources,gint search_offset,gint search_limit,gboolean show_resources)472 get_all_tags (TrackerSparqlConnection *connection,
473 GStrv resources,
474 gint search_offset,
475 gint search_limit,
476 gboolean show_resources)
477 {
478 TrackerSparqlCursor *cursor;
479 GError *error = NULL;
480 gchar *query;
481 gchar *filter = NULL;
482
483 if (resources && g_strv_length (resources) > 0) {
484 filter = get_filter_in_for_strv (resources, "?label");
485 }
486
487 /* You might be asking, why not logical AND here, why
488 * logical OR for FILTER, well, tags can't have
489 * multiple labels is the simple answer.
490 */
491 query = g_strdup_printf ("SELECT ?tag ?label nao:description(?tag) COUNT(?urns) "
492 "WHERE {"
493 " ?tag a nao:Tag ;"
494 " nao:prefLabel ?label ."
495 " OPTIONAL {"
496 " ?urns nao:hasTag ?tag"
497 " } ."
498 " %s"
499 "} "
500 "GROUP BY ?tag "
501 "ORDER BY ASC(?label) "
502 "OFFSET %d "
503 "LIMIT %d",
504 filter ? filter : "",
505 search_offset,
506 search_limit);
507 g_free (filter);
508
509 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
510 g_free (query);
511
512 if (error) {
513 g_printerr ("%s, %s\n",
514 _("Could not get all tags"),
515 error->message);
516 g_error_free (error);
517
518 return FALSE;
519 }
520
521 if (!cursor) {
522 g_print ("%s\n",
523 _("No tags were found"));
524 } else {
525 gint count = 0;
526
527 g_print ("%s:\n", _("Tags (shown by name)"));
528
529 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
530 const gchar *id;
531 const gchar *tag;
532 const gchar *description;
533 const gchar *resources;
534 gint n_resources = 0;
535
536 id = tracker_sparql_cursor_get_string (cursor, 0, NULL);
537 resources = tracker_sparql_cursor_get_string (cursor, 3, NULL);
538 n_resources = atoi (resources);
539
540 tag = tracker_sparql_cursor_get_string (cursor, 1, NULL);
541 description = tracker_sparql_cursor_get_string (cursor, 2, NULL);
542
543 if (description && *description == '\0') {
544 description = NULL;
545 }
546
547 g_print (" %s %s%s%s\n",
548 tag,
549 description ? "(" : "",
550 description ? description : "",
551 description ? ")" : "");
552
553 if (show_resources && n_resources > 0) {
554 get_all_tags_show_tag_id (connection, id);
555 } else {
556 g_print (" %s\n", id);
557 g_print (" ");
558 g_print (g_dngettext (NULL,
559 "%d file",
560 "%d files",
561 n_resources),
562 n_resources);
563 g_print ("\n");
564 }
565
566 count++;
567 }
568
569 if (count == 0) {
570 /* To translators: This is to say there are no
571 * resources found associated with this tag, e.g.:
572 *
573 * Tags (shown by name):
574 * None
575 *
576 */
577 g_print (" %s\n", _("None"));
578 }
579
580 g_print ("\n");
581
582 if (count >= search_limit) {
583 show_limit_warning ();
584 }
585
586 g_object_unref (cursor);
587 }
588
589 return TRUE;
590 }
591
592 static void
print_file_report(TrackerSparqlCursor * cursor,GStrv uris,const gchar * found_msg,const gchar * not_found_msg)593 print_file_report (TrackerSparqlCursor *cursor,
594 GStrv uris,
595 const gchar *found_msg,
596 const gchar *not_found_msg)
597 {
598 gint i;
599
600 if (!cursor || !uris) {
601 g_print (" %s\n", _("No files were modified"));
602 return;
603 }
604
605 for (i = 0; uris[i]; i++) {
606 gboolean found = FALSE;
607
608 tracker_sparql_cursor_rewind (cursor);
609
610 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
611 const gchar *str;
612
613 str = tracker_sparql_cursor_get_string (cursor, 1, NULL);
614
615 if (g_strcmp0 (str, uris[i]) == 0) {
616 found = TRUE;
617 break;
618 }
619 }
620
621 g_print (" %s: %s\n",
622 found ? found_msg : not_found_msg,
623 uris[i]);
624 }
625 }
626
627 static gboolean
add_tag_for_urns(TrackerSparqlConnection * connection,GStrv resources,const gchar * tag,const gchar * description)628 add_tag_for_urns (TrackerSparqlConnection *connection,
629 GStrv resources,
630 const gchar *tag,
631 const gchar *description)
632 {
633 TrackerSparqlCursor *cursor = NULL;
634 GError *error = NULL;
635 GStrv uris = NULL, urns_strv = NULL;
636 gchar *tag_escaped;
637 gchar *query;
638
639 tag_escaped = get_escaped_sparql_string (tag);
640
641 if (resources) {
642 uris = get_uris (resources);
643
644 if (!uris) {
645 return FALSE;
646 }
647
648 cursor = get_file_urns (connection, uris, NULL);
649
650 if (!cursor) {
651 g_printerr ("%s\n", _("Files do not exist or aren’t indexed"));
652 g_strfreev (uris);
653 return FALSE;
654 }
655
656 urns_strv = result_to_strv (cursor, 0);
657
658 if (!urns_strv || g_strv_length (urns_strv) < 1) {
659 g_printerr ("%s\n", _("Files do not exist or aren’t indexed"));
660 g_object_unref (cursor);
661 g_strfreev (uris);
662 return FALSE;
663 }
664 }
665
666 if (description) {
667 gchar *description_escaped;
668
669 description_escaped = get_escaped_sparql_string (description);
670
671 query = g_strdup_printf ("INSERT { "
672 " _:tag a nao:Tag;"
673 " nao:prefLabel %s ;"
674 " nao:description %s ."
675 "} "
676 "WHERE {"
677 " OPTIONAL {"
678 " ?tag a nao:Tag ;"
679 " nao:prefLabel %s ."
680 " } ."
681 " FILTER (!bound(?tag)) "
682 "}",
683 tag_escaped,
684 description_escaped,
685 tag_escaped);
686
687 g_free (description_escaped);
688 } else {
689 query = g_strdup_printf ("INSERT { "
690 " _:tag a nao:Tag;"
691 " nao:prefLabel %s ."
692 "} "
693 "WHERE {"
694 " OPTIONAL {"
695 " ?tag a nao:Tag ;"
696 " nao:prefLabel %s ."
697 " } ."
698 " FILTER (!bound(?tag)) "
699 "}",
700 tag_escaped,
701 tag_escaped);
702 }
703
704 tracker_sparql_connection_update (connection, query, 0, NULL, &error);
705 g_free (query);
706
707 if (error) {
708 g_printerr ("%s, %s\n",
709 _("Could not add tag"),
710 error->message);
711
712 if (cursor) {
713 g_object_unref (cursor);
714 }
715
716 g_error_free (error);
717 g_free (tag_escaped);
718 g_strfreev (urns_strv);
719 g_strfreev (uris);
720
721 return FALSE;
722 }
723
724 g_print ("%s\n",
725 _("Tag was added successfully"));
726
727 /* First we check if the tag is already set and only add if it
728 * is, then we add the urns specified to the new tag.
729 */
730 if (urns_strv) {
731 gchar *filter;
732
733 filter = get_filter_string (urns_strv, "?urn", TRUE, NULL);
734
735 /* Add tag to specific urns */
736 query = g_strdup_printf ("INSERT { "
737 " ?urn nao:hasTag ?id "
738 "} "
739 "WHERE {"
740 " ?urn nie:url ?f ."
741 " ?id nao:prefLabel %s "
742 " %s "
743 "}",
744 tag_escaped,
745 filter ? filter : "");
746
747 tracker_sparql_connection_update (connection, query, 0, NULL, &error);
748 g_strfreev (urns_strv);
749 g_free (filter);
750 g_free (query);
751
752 if (error) {
753 g_printerr ("%s, %s\n",
754 _("Could not add tag to files"),
755 error->message);
756 g_object_unref (cursor);
757 g_error_free (error);
758 g_free (tag_escaped);
759 g_strfreev (uris);
760
761 return FALSE;
762 }
763
764 print_file_report (cursor, uris, _("Tagged"),
765 _("Not tagged, file is not indexed"));
766 }
767
768 g_strfreev (uris);
769 g_free (tag_escaped);
770
771 if (cursor) {
772 g_object_unref (cursor);
773 }
774
775 return TRUE;
776 }
777
778 static gboolean
remove_tag_for_urns(TrackerSparqlConnection * connection,GStrv resources,const gchar * tag)779 remove_tag_for_urns (TrackerSparqlConnection *connection,
780 GStrv resources,
781 const gchar *tag)
782 {
783 TrackerSparqlCursor *urns_cursor = NULL;
784 GError *error = NULL;
785 gchar *tag_escaped;
786 gchar *query;
787 GStrv uris;
788
789 tag_escaped = get_escaped_sparql_string (tag);
790 uris = get_uris (resources);
791
792 if (uris && *uris) {
793 TrackerSparqlCursor *tag_cursor;
794 gchar *filter;
795 const gchar *urn;
796 GStrv urns_strv;
797
798 /* Get all tags urns */
799 query = g_strdup_printf ("SELECT ?tag "
800 "WHERE {"
801 " ?tag a nao:Tag ."
802 " ?tag nao:prefLabel %s "
803 "}",
804 tag_escaped);
805
806 tag_cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
807 g_free (query);
808
809 if (error) {
810 g_printerr ("%s, %s\n",
811 _("Could not get tag by label"),
812 error->message);
813 g_error_free (error);
814 g_free (tag_escaped);
815 g_strfreev (uris);
816
817 return FALSE;
818 }
819
820 if (!tag_cursor || !tracker_sparql_cursor_next (tag_cursor, NULL, NULL)) {
821 g_print ("%s\n",
822 _("No tags were found by that name"));
823
824 g_free (tag_escaped);
825 g_strfreev (uris);
826
827 if (tag_cursor) {
828 g_object_unref (tag_cursor);
829 }
830
831 return TRUE;
832 }
833
834 urn = tracker_sparql_cursor_get_string (tag_cursor, 0, NULL);
835 urns_cursor = get_file_urns (connection, uris, urn);
836
837 if (!urns_cursor || !tracker_sparql_cursor_next (urns_cursor, NULL, NULL)) {
838 g_print ("%s\n",
839 _("None of the files had this tag set"));
840
841 g_strfreev (uris);
842 g_free (tag_escaped);
843 g_object_unref (tag_cursor);
844
845 if (urns_cursor) {
846 g_object_unref (urns_cursor);
847 }
848
849 return TRUE;
850 }
851
852 urns_strv = result_to_strv (urns_cursor, 0);
853 filter = get_filter_string (urns_strv, "?urn", TRUE, urn);
854 g_strfreev (urns_strv);
855
856 query = g_strdup_printf ("DELETE { "
857 " ?urn nao:hasTag ?t "
858 "} "
859 "WHERE { "
860 " ?urn nao:hasTag ?t . "
861 " %s "
862 "}",
863 filter ? filter : "");
864 g_free (filter);
865
866 g_object_unref (tag_cursor);
867 } else {
868 /* Remove tag completely */
869 query = g_strdup_printf ("DELETE { "
870 " ?tag a nao:Tag "
871 "} "
872 "WHERE {"
873 " ?tag nao:prefLabel %s "
874 "}",
875 tag_escaped);
876 }
877
878 g_free (tag_escaped);
879
880 tracker_sparql_connection_update (connection, query, 0, NULL, &error);
881 g_free (query);
882
883 if (error) {
884 g_printerr ("%s, %s\n",
885 _("Could not remove tag"),
886 error->message);
887 g_error_free (error);
888
889 return FALSE;
890 }
891
892 g_print ("%s\n", _("Tag was removed successfully"));
893
894 if (urns_cursor) {
895 print_file_report (urns_cursor, uris,
896 _("Untagged"),
897 _("File not indexed or already untagged"));
898 g_object_unref (urns_cursor);
899 }
900
901 g_strfreev (uris);
902
903 return TRUE;
904 }
905
906 static gboolean
get_tags_by_file(TrackerSparqlConnection * connection,const gchar * uri)907 get_tags_by_file (TrackerSparqlConnection *connection,
908 const gchar *uri)
909 {
910 TrackerSparqlCursor *cursor;
911 GError *error = NULL;
912 gchar *query;
913
914 query = g_strdup_printf ("SELECT ?tags ?labels "
915 "WHERE {"
916 " ?urn nao:hasTag ?tags ;"
917 " nie:url <%s> ."
918 " ?tags a nao:Tag ;"
919 " nao:prefLabel ?labels "
920 "} "
921 "ORDER BY ASC(?labels)",
922 uri);
923
924 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
925 g_free (query);
926
927 if (error) {
928 g_printerr ("%s, %s\n",
929 _("Could not get all tags"),
930 error->message);
931 g_error_free (error);
932
933 return FALSE;
934 }
935
936 if (!cursor) {
937 g_print (" %s\n",
938 _("No tags were found"));
939 } else {
940 gint count = 0;
941
942 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
943 g_print (" %s\n", tracker_sparql_cursor_get_string (cursor, 1, NULL));
944 count++;
945 }
946
947 if (count == 0) {
948 /* To translators: This is to say there are no
949 * tags found for a particular file, e.g.:
950 *
951 * /path/to/some/file:
952 * None
953 *
954 */
955 g_print (" %s\n", _("None"));
956 }
957
958 g_print ("\n");
959
960 g_object_unref (cursor);
961 }
962
963 return TRUE;
964 }
965
966 static int
tag_run(void)967 tag_run (void)
968 {
969 TrackerSparqlConnection *connection;
970 GError *error = NULL;
971
972 connection = tracker_sparql_connection_get (NULL, &error);
973
974 if (!connection) {
975 g_printerr ("%s: %s\n",
976 _("Could not establish a connection to Tracker"),
977 error ? error->message : _("No error given"));
978 g_clear_error (&error);
979 return EXIT_FAILURE;
980 }
981
982 if (list) {
983 gboolean success;
984
985 if (G_UNLIKELY (and_operator)) {
986 success = get_all_resources_with_tags (connection, resources, offset, limit);
987 } else {
988 success = get_all_tags (connection, resources, offset, limit, show_resources);
989 }
990 g_object_unref (connection);
991
992 return success ? EXIT_SUCCESS : EXIT_FAILURE;
993 }
994
995 if (add_tag) {
996 gboolean success;
997
998 success = add_tag_for_urns (connection, resources, add_tag, description);
999 g_object_unref (connection);
1000
1001 return success ? EXIT_SUCCESS : EXIT_FAILURE;
1002 }
1003
1004 if (remove_tag) {
1005 gboolean success;
1006
1007 success = remove_tag_for_urns (connection, resources, remove_tag);
1008 g_object_unref (connection);
1009
1010 return success ? EXIT_SUCCESS : EXIT_FAILURE;
1011 }
1012
1013 if (resources) {
1014 gboolean success = TRUE;
1015 gchar **p;
1016
1017 for (p = resources; *p; p++) {
1018 GFile *file;
1019 gchar *uri;
1020
1021 file = g_file_new_for_commandline_arg (*p);
1022 uri = g_file_get_uri (file);
1023 g_object_unref (file);
1024
1025 g_print ("%s\n", uri);
1026 success &= get_tags_by_file (connection, uri);
1027
1028 g_free (uri);
1029 }
1030
1031 g_object_unref (connection);
1032
1033 return success ? EXIT_SUCCESS : EXIT_FAILURE;
1034 }
1035
1036 g_object_unref (connection);
1037
1038 /* This is a failure because we should have done something.
1039 * This code should never be reached in practise.
1040 */
1041 return EXIT_FAILURE;
1042 }
1043
1044 static int
tag_run_default(void)1045 tag_run_default (void)
1046 {
1047 GOptionContext *context;
1048 gchar *help;
1049
1050 context = g_option_context_new (NULL);
1051 g_option_context_add_main_entries (context, entries, NULL);
1052 help = g_option_context_get_help (context, TRUE, NULL);
1053 g_option_context_free (context);
1054 g_printerr ("%s\n", help);
1055 g_free (help);
1056
1057 return EXIT_FAILURE;
1058 }
1059
1060 static gboolean
tag_options_enabled(void)1061 tag_options_enabled (void)
1062 {
1063 return TAG_OPTIONS_ENABLED ();
1064 }
1065
1066 int
tracker_tag(int argc,const char ** argv)1067 tracker_tag (int argc, const char **argv)
1068 {
1069 GOptionContext *context;
1070 GError *error = NULL;
1071 const gchar *failed;
1072
1073 context = g_option_context_new (NULL);
1074 g_option_context_add_main_entries (context, entries, NULL);
1075
1076 argv[0] = "tracker tag";
1077
1078 if (!g_option_context_parse (context, &argc, (char***) &argv, &error)) {
1079 g_printerr ("%s, %s\n", _("Unrecognized options"), error->message);
1080 g_error_free (error);
1081 g_option_context_free (context);
1082 return EXIT_FAILURE;
1083 }
1084
1085 g_option_context_free (context);
1086
1087 if (!list && show_resources) {
1088 failed = _("The --list option is required for --show-files");
1089 } else if (and_operator && (!list || !resources)) {
1090 failed = _("The --and-operator option can only be used with --list and tag label arguments");
1091 } else if (add_tag && remove_tag) {
1092 failed = _("Add and delete actions can not be used together");
1093 } else if (description && !add_tag) {
1094 failed = _("The --description option can only be used with --add");
1095 } else {
1096 failed = NULL;
1097 }
1098
1099 if (failed) {
1100 g_printerr ("%s\n\n", failed);
1101 return EXIT_FAILURE;
1102 }
1103
1104 if (tag_options_enabled ()) {
1105 return tag_run ();
1106 }
1107
1108 return tag_run_default ();
1109 }
1110