No issues found
1 /*
2 * Copyright (C) 2006, Jamie McCracken <jamiemcc@gnome.org>
3 * Copyright (C) 2008-2010, Nokia <ivan.frade@nokia.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 <string.h>
25 #include <time.h>
26 #include <locale.h>
27
28 #include <glib.h>
29 #include <glib/gi18n.h>
30
31 #include <libtracker-sparql/tracker-sparql.h>
32
33 #define ABOUT \
34 "Tracker " PACKAGE_VERSION "\n"
35
36 #define LICENSE \
37 "This program is free software and comes without any warranty.\n" \
38 "It is licensed under version 2 or later of the General Public " \
39 "License which can be viewed at:\n" \
40 "\n" \
41 " http://www.gnu.org/licenses/gpl.txt\n"
42
43 static gchar **filenames;
44 static gboolean full_namespaces;
45 static gboolean print_version;
46 static gboolean plain_text_content;
47 static gboolean turtle;
48
49 static GOptionEntry entries[] = {
50 { "version", 'V', 0, G_OPTION_ARG_NONE, &print_version,
51 N_("Print version"),
52 NULL,
53 },
54 { "full-namespaces", 'f', 0, G_OPTION_ARG_NONE, &full_namespaces,
55 N_("Show full namespaces (i.e. don't use nie:title, use full URLs)"),
56 NULL,
57 },
58 { "plain-text-content", 'c', 0, G_OPTION_ARG_NONE, &plain_text_content,
59 N_("Show plain text content if available for resources"),
60 NULL,
61 },
62 { "turtle", 't', 0, G_OPTION_ARG_NONE, &turtle,
63 N_("Output results as RDF in Turtle format"),
64 NULL,
65 },
66 { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames,
67 N_("FILE"),
68 N_("FILE")},
69 { NULL }
70 };
71
72 static gchar *
73 get_shorthand (GHashTable *prefixes,
74 const gchar *namespace)
75 {
76 gchar *hash;
77
78 hash = strrchr (namespace, '#');
79
80 if (hash) {
81 gchar *property;
82 const gchar *prefix;
83
84 property = hash + 1;
85 *hash = '\0';
86
87 prefix = g_hash_table_lookup (prefixes, namespace);
88
89 return g_strdup_printf ("%s:%s", prefix, property);
90 }
91
92 return g_strdup (namespace);
93 }
94
95 static gboolean
96 has_valid_uri_scheme (const gchar *uri)
97 {
98 const gchar *s;
99
100 s = uri;
101
102 if (!g_ascii_isalpha (*s)) {
103 return FALSE;
104 }
105
106 do {
107 s++;
108 } while (g_ascii_isalnum (*s) || *s == '+' || *s == '.' || *s == '-');
109
110 return (*s == ':');
111 }
112
113 static GHashTable *
114 get_prefixes (TrackerSparqlConnection *connection)
115 {
116 TrackerSparqlCursor *cursor;
117 GError *error = NULL;
118 GHashTable *retval;
119 const gchar *query;
120
121 retval = g_hash_table_new_full (g_str_hash,
122 g_str_equal,
123 g_free,
124 g_free);
125
126 /* FIXME: Would like to get this in the same SPARQL that we
127 * use to get the info, but doesn't seem possible at the
128 * moment with the limited string manipulation features we
129 * support in SPARQL.
130 */
131 query = "SELECT ?ns ?prefix "
132 "WHERE {"
133 " ?ns a tracker:Namespace ;"
134 " tracker:prefix ?prefix "
135 "}";
136
137 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
138
139 if (error) {
140 g_printerr ("%s, %s\n",
141 _("Unable to retrieve namespace prefixes"),
142 error->message);
143
144 g_error_free (error);
145 return retval;
146 }
147
148 if (!cursor) {
149 g_printerr ("%s\n", _("No namespace prefixes were returned"));
150 return retval;
151 }
152
153 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
154 const gchar *key, *value;
155
156 key = tracker_sparql_cursor_get_string (cursor, 0, NULL);
157 value = tracker_sparql_cursor_get_string (cursor, 1, NULL);
158
159 if (!key || !value) {
160 continue;
161 }
162
163 g_hash_table_insert (retval,
164 g_strndup (key, strlen (key) - 1),
165 g_strdup (value));
166 }
167
168 if (cursor) {
169 g_object_unref (cursor);
170 }
171
172 return retval;
173 }
174
175 static inline void
176 print_key_and_value (GHashTable *prefixes,
177 const gchar *key,
178 const gchar *value)
179 {
180 if (G_UNLIKELY (full_namespaces)) {
181 g_print (" '%s' = '%s'\n", key, value);
182 } else {
183 gchar *shorthand;
184
185 shorthand = get_shorthand (prefixes, key);
186 g_print (" '%s' = '%s'\n", shorthand, value);
187 g_free (shorthand);
188 }
189 }
190
191 static void
192 print_plain (gchar *urn_or_filename,
193 gchar *urn,
194 TrackerSparqlCursor *cursor,
195 GHashTable *prefixes,
196 gboolean full_namespaces)
197 {
198 gchar *fts_key = NULL;
199 gchar *fts_value = NULL;
200
201 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
202 const gchar *key = tracker_sparql_cursor_get_string (cursor, 0, NULL);
203 const gchar *value = tracker_sparql_cursor_get_string (cursor, 1, NULL);
204
205 if (!key || !value) {
206 continue;
207 }
208
209 /* Don't display nie:plainTextContent */
210 if (strcmp (key, "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#plainTextContent") == 0) {
211 if (plain_text_content) {
212 fts_key = g_strdup (key);
213 fts_value = g_strdup (value);
214 }
215
216 /* Always print FTS data at the end because of it's length */
217 continue;
218 }
219
220 print_key_and_value (prefixes, key, value);
221 }
222
223 if (fts_key && fts_value) {
224 print_key_and_value (prefixes, fts_key, fts_value);
225 }
226
227 g_free (fts_key);
228 g_free (fts_value);
229 }
230
231 /* print a URI prefix in Turtle format */
232 static void
233 print_prefix (gpointer key,
234 gpointer value,
235 gpointer user_data)
236 {
237 g_print ("@prefix %s: <%s#> .\n", (gchar *) value, (gchar *) key);
238 }
239
240 /* format a URI for Turtle; if it has a prefix, display uri
241 * as prefix:rest_of_uri; if not, display as <uri>
242 */
243 inline static gchar *
244 format_urn (GHashTable *prefixes,
245 const gchar *urn,
246 gboolean full_namespaces)
247 {
248 gchar *urn_out;
249
250 if (full_namespaces) {
251 urn_out = g_strdup_printf ("<%s>", urn);
252 } else {
253 gchar *shorthand = get_shorthand (prefixes, urn);
254
255 /* If the shorthand is the same as the urn passed, we
256 * assume it is a resource and pass it in as one,
257 *
258 * e.g.: http://purl.org/dc/elements/1.1/date
259 * to: http://purl.org/dc/elements/1.1/date
260 *
261 * Otherwise, we use the shorthand version instead.
262 *
263 * e.g.: http://www.w3.org/1999/02/22-rdf-syntax-ns
264 * to: rdf
265 */
266 if (g_strcmp0 (shorthand, urn) == 0) {
267 urn_out = g_strdup_printf ("<%s>", urn);
268 g_free (shorthand);
269 } else {
270 urn_out = shorthand;
271 }
272 }
273
274 return urn_out;
275 }
276
277 /* Print triples for a urn in Turtle format */
278 static void
279 print_turtle (gchar *urn,
280 TrackerSparqlCursor *cursor,
281 GHashTable *prefixes,
282 gboolean full_namespaces)
283 {
284 gchar *subject;
285 gchar *predicate;
286 gchar *object;
287
288 if (G_UNLIKELY (full_namespaces)) {
289 subject = g_strdup (urn);
290 } else {
291 /* truncate subject */
292 subject = get_shorthand (prefixes, urn);
293 }
294
295 while (tracker_sparql_cursor_next (cursor, NULL, NULL)) {
296 const gchar *key = tracker_sparql_cursor_get_string (cursor, 0, NULL);
297 const gchar *value = tracker_sparql_cursor_get_string (cursor, 1, NULL);
298 const gchar *is_resource = tracker_sparql_cursor_get_string (cursor, 2, NULL);
299
300 if (!key || !value || !is_resource) {
301 continue;
302 }
303
304 /* Don't display nie:plainTextContent */
305 if (!plain_text_content && strcmp (key, "http://www.semanticdesktop.org/ontologies/2007/01/19/nie#plainTextContent") == 0) {
306 continue;
307 }
308
309 predicate = format_urn (prefixes, key, full_namespaces);
310
311 if (g_ascii_strcasecmp (is_resource, "true") == 0) {
312 object = g_strdup_printf ("<%s>", value);
313 } else {
314 gchar *escaped_value;
315
316 /* Escape value and make sure it is encapsulated properly */
317 escaped_value = tracker_sparql_escape_string (value);
318 object = g_strdup_printf ("\"%s\"", escaped_value);
319 g_free (escaped_value);
320 }
321
322 /* Print final statement */
323 g_print ("<%s> %s %s .\n", subject, predicate, object);
324
325 g_free (predicate);
326 g_free (object);
327 }
328
329 g_free (subject);
330 }
331
332 int
333 main (int argc, char **argv)
334 {
335 TrackerSparqlConnection *connection;
336 GOptionContext *context;
337 GError *error = NULL;
338 GHashTable *prefixes;
339 gchar **p;
340
341 setlocale (LC_ALL, "");
342
343 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
344 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
345 textdomain (GETTEXT_PACKAGE);
346
347 /* Translators: this messagge will apper immediately after the */
348 /* usage string - Usage: COMMAND [OPTION]... <THIS_MESSAGE> */
349 context = g_option_context_new (_("- Get all information about one or more files"));
350
351 /* Translators: this message will appear after the usage string */
352 /* and before the list of options. */
353 g_option_context_add_main_entries (context, entries, NULL);
354 g_option_context_parse (context, &argc, &argv, NULL);
355
356 if (print_version) {
357 g_print ("\n" ABOUT "\n" LICENSE "\n");
358 g_option_context_free (context);
359
360 return EXIT_SUCCESS;
361 }
362
363 if (!filenames) {
364 gchar *help;
365
366 g_printerr ("%s\n\n",
367 _("One or more files have not been specified"));
368
369 help = g_option_context_get_help (context, TRUE, NULL);
370 g_option_context_free (context);
371 g_printerr ("%s", help);
372 g_free (help);
373
374 return EXIT_FAILURE;
375 }
376
377 g_option_context_free (context);
378
379 connection = tracker_sparql_connection_get (NULL, &error);
380
381 if (!connection) {
382 g_printerr ("%s: %s\n",
383 _("Could not establish a connection to Tracker"),
384 error ? error->message : _("No error given"));
385 g_clear_error (&error);
386 return EXIT_FAILURE;
387 }
388
389 prefixes = get_prefixes (connection);
390
391 /* print all prefixes if using turtle format and not showing full namespaces */
392 if (turtle && !full_namespaces) {
393 g_hash_table_foreach (prefixes, (GHFunc) print_prefix, NULL);
394 g_print ("\n");
395 }
396
397 for (p = filenames; *p; p++) {
398 TrackerSparqlCursor *cursor;
399 GError *error = NULL;
400 gchar *uri;
401 gchar *query;
402 gchar *urn = NULL;
403
404 if (!turtle) {
405 g_print ("%s:'%s'\n", _("Querying information for entity"), *p);
406 }
407
408 /* support both, URIs and local file paths */
409 if (has_valid_uri_scheme (*p)) {
410 uri = g_strdup (*p);
411 } else {
412 GFile *file;
413
414 file = g_file_new_for_commandline_arg (*p);
415 uri = g_file_get_uri (file);
416 g_object_unref (file);
417 }
418
419 /* First check whether there's some entity with nie:url like this */
420 query = g_strdup_printf ("SELECT ?urn WHERE { ?urn nie:url \"%s\" }", uri);
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 _("Unable to retrieve URN for URI"),
427 error->message);
428
429 g_clear_error (&error);
430 continue;
431 }
432
433 if (!cursor || !tracker_sparql_cursor_next (cursor, NULL, &error)) {
434 if (error) {
435 g_printerr (" %s, %s\n",
436 _("Unable to retrieve data for URI"),
437 error->message);
438
439 g_object_unref (cursor);
440 g_clear_error (&error);
441
442 continue;
443 }
444
445 /* No URN matches, use uri as URN */
446 urn = g_strdup (uri);
447 } else {
448 urn = g_strdup (tracker_sparql_cursor_get_string (cursor, 0, NULL));
449
450 if (!turtle) {
451 g_print (" '%s'\n", urn);
452 }
453
454 g_object_unref (cursor);
455 }
456
457 query = g_strdup_printf ("SELECT ?predicate ?object"
458 " ( EXISTS { ?predicate rdfs:range [ rdfs:subClassOf rdfs:Resource ] } )"
459 "WHERE {"
460 " <%s> ?predicate ?object "
461 "}",
462 urn);
463
464 cursor = tracker_sparql_connection_query (connection, query, NULL, &error);
465
466 g_free (uri);
467 g_free (query);
468
469 if (error) {
470 g_printerr (" %s, %s\n",
471 _("Unable to retrieve data for URI"),
472 error->message);
473
474 g_clear_error (&error);
475 continue;
476 }
477
478 if (!cursor) {
479 g_print (" %s\n",
480 _("No metadata available for that URI"));
481 } else {
482 if (turtle) {
483 print_turtle (urn, cursor, prefixes, full_namespaces);
484 } else {
485 g_print ("%s:\n", _("Results"));
486
487 print_plain (*p, urn, cursor, prefixes, full_namespaces);
488 }
489
490 g_print ("\n");
491
492 g_object_unref (cursor);
493 }
494
495 g_print ("\n");
496
497 g_free (urn);
498 }
499
500 g_hash_table_unref (prefixes);
501 g_object_unref (connection);
502
503 return EXIT_SUCCESS;
504 }