aboutsummaryrefslogtreecommitdiff
path: root/src/fs
diff options
context:
space:
mode:
authormadmurphy <madmurphy333@gmail.com>2022-02-10 22:24:56 +0000
committermadmurphy <madmurphy333@gmail.com>2022-02-10 22:24:56 +0000
commit4b887d8a8a849a8c878934009976dd47300b43cb (patch)
tree02f8d705b83f1d8d1085bf74f2092b26fb484502 /src/fs
parent82e288bcaa328b4cf39208646c2efb3dac0ccd1f (diff)
downloadgnunet-4b887d8a8a849a8c878934009976dd47300b43cb.tar.gz
gnunet-4b887d8a8a849a8c878934009976dd47300b43cb.zip
New version of `gnunet-search` is ready to be tested
Diffstat (limited to 'src/fs')
-rw-r--r--src/fs/gnunet-search.c616
1 files changed, 524 insertions, 92 deletions
diff --git a/src/fs/gnunet-search.c b/src/fs/gnunet-search.c
index 3bf013650..124a0a1a4 100644
--- a/src/fs/gnunet-search.c
+++ b/src/fs/gnunet-search.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V. 3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 2022 GNUnet e.V.
4 4
5 GNUnet is free software: you can redistribute it and/or modify it 5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published 6 under the terms of the GNU Affero General Public License as published
@@ -24,10 +24,57 @@
24 * @author Krista Bennett 24 * @author Krista Bennett
25 * @author James Blackwell 25 * @author James Blackwell
26 * @author Igor Wronsky 26 * @author Igor Wronsky
27 * @author madmurphy
27 */ 28 */
29#include <ctype.h>
30#include <inttypes.h>
31#include <limits.h>
28#include "platform.h" 32#include "platform.h"
29#include "gnunet_fs_service.h" 33#include "gnunet_fs_service.h"
30 34
35
36#define GNUNET_SEARCH_log(kind, ...) \
37 GNUNET_log_from(kind, "gnunet-search", __VA_ARGS__)
38
39
40/* The default settings that we use for the printed output */
41
42#define DEFAULT_DIR_FORMAT "#%n:\ngnunet-download -o \"%f\" -R %u\n\n"
43#define HELP_DEFAULT_DIR_FORMAT "#%n:\\ngnunet-download -o \"%f\" -R %u\\n\\n"
44#define DEFAULT_FILE_FORMAT "#%n:\ngnunet-download -o \"%f\" %u\n\n"
45#define HELP_DEFAULT_FILE_FORMAT "#%n:\\ngnunet-download -o \"%f\" %u\\n\\n"
46#define VERB_DEFAULT_DIR_FORMAT DEFAULT_DIR_FORMAT "%a\n"
47#define VERB_DEFAULT_FILE_FORMAT DEFAULT_FILE_FORMAT "%a\n"
48
49#if HAVE_LIBEXTRACTOR
50#define DEFAULT_META_FORMAT " %t: %p\n"
51#define HELP_DEFAULT_META_FORMAT " %t: %p\\n"
52#define HELP_EXTRACTOR_TEXTADD ", %t is the property type"
53#else
54#define DEFAULT_META_FORMAT " MetaType #%i: %p\n"
55#define HELP_DEFAULT_META_FORMAT " MetaType #%i: %p\\n"
56#define HELP_EXTRACTOR_TEXTADD ""
57#endif
58
59#define GENERIC_DIRECTORY_NAME "collection"
60#define GENERIC_FILE_NAME "no-name"
61#define GENERIC_FILE_MIMETYPE "application/octet-stream"
62
63
64enum GNUNET_SEARCH_MetadataPrinterFlags {
65 METADATA_PRINTER_FLAG_NONE = 0,
66 METADATA_PRINTER_FLAG_ONE_RUN = 1,
67 METADATA_PRINTER_FLAG_HAVE_TYPE = 2
68};
69
70
71struct GNUNET_SEARCH_MetadataPrinterInfo {
72 unsigned int counter;
73 unsigned int flags;
74 int type;
75};
76
77
31static int ret; 78static int ret;
32 79
33static const struct GNUNET_CONFIGURATION_Handle *cfg; 80static const struct GNUNET_CONFIGURATION_Handle *cfg;
@@ -38,6 +85,12 @@ static struct GNUNET_FS_SearchContext *sc;
38 85
39static char *output_filename; 86static char *output_filename;
40 87
88static char *format_string;
89
90static char *dir_format_string;
91
92static char *meta_format_string;
93
41static struct GNUNET_FS_DirectoryBuilder *db; 94static struct GNUNET_FS_DirectoryBuilder *db;
42 95
43static unsigned int anonymity = 1; 96static unsigned int anonymity = 1;
@@ -53,16 +106,79 @@ static unsigned int results;
53 106
54static unsigned int verbose; 107static unsigned int verbose;
55 108
109static int bookmark_only;
110
56static int local_only; 111static int local_only;
57 112
113static int silent_mode;
114
58static struct GNUNET_SCHEDULER_Task *tt; 115static struct GNUNET_SCHEDULER_Task *tt;
59 116
60 117
61/** 118/**
119 * Print the escape sequence at the beginning of a string.
120 *
121 * @param esc a string that **must** begin with a backslash (the function only
122 * assumes that it does, but does not check)
123 * @return the fragment that follows what has been printed
124 * @author madmurphy
125 *
126 * If `"\\nfoo"` is passed as argument, this function prints a new line and
127 * returns `"foo"`
128 */
129static const char *
130print_escape_sequence (const char *const esc)
131{
132 unsigned int probe;
133 const char * cursor = esc + 1;
134 char tmp;
135 switch (*cursor)
136 {
137 /* Trivia */
138 case '\\': putchar ('\\'); return cursor + 1;
139 case 'a': putchar ('\a'); return cursor + 1;
140 case 'b': putchar ('\b'); return cursor + 1;
141 case 'e': putchar ('\x1B'); return cursor + 1;
142 case 'f': putchar ('\f'); return cursor + 1;
143 case 'n': putchar ('\n'); return cursor + 1;
144 case 'r': putchar ('\r'); return cursor + 1;
145 case 't': putchar ('\t'); return cursor + 1;
146 case 'v': putchar ('\v'); return cursor + 1;
147
148 /* Possibly hexadecimal code point */
149 case 'x':
150 probe = 0;
151 while (probe < 256 && isxdigit((tmp = *++cursor)))
152 probe = (probe << 4) + tmp - (tmp > 96 ? 87 : tmp > 64 ? 55 : 48);
153 goto maybe_codepoint;
154
155 /* Possibly octal code point */
156 case '0': case '1': case '2': case '3':
157 case '4': case '5': case '6': case '7':
158 probe = *cursor++ - 48;
159 do probe = (probe << 3) + *cursor++ - 48;
160 while (probe < 256 && cursor < esc + 4 && *cursor > 47 && *cursor < 56);
161 goto maybe_codepoint;
162
163 /* Boredom */
164 case '\0': putchar ('\\'); return cursor;
165 default: printf ("\\%c", *cursor); return cursor + 1;
166 }
167
168maybe_codepoint:
169 if (probe < 256)
170 putchar (probe);
171 else
172 fwrite (esc, 1, cursor - esc, stdout);
173 return cursor;
174}
175
176
177/**
62 * Type of a function that libextractor calls for each 178 * Type of a function that libextractor calls for each
63 * meta data item found. 179 * meta data item found.
64 * 180 *
65 * @param cls closure (user-defined, unused) 181 * @param cls closure (user-defined, used for the iteration info)
66 * @param plugin_name name of the plugin that produced this value; 182 * @param plugin_name name of the plugin that produced this value;
67 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being 183 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
68 * used in the main libextractor library and yielding 184 * used in the main libextractor library and yielding
@@ -76,33 +192,228 @@ static struct GNUNET_SCHEDULER_Task *tt;
76 * @return 0 to continue extracting, 1 to abort 192 * @return 0 to continue extracting, 1 to abort
77 */ 193 */
78static int 194static int
79item_printer (void *cls, 195item_printer (void *const cls,
80 const char *plugin_name, 196 const char *const plugin_name,
81 enum EXTRACTOR_MetaType type, 197 const enum EXTRACTOR_MetaType type,
82 enum EXTRACTOR_MetaFormat format, 198 const enum EXTRACTOR_MetaFormat format,
83 const char *data_mime_type, 199 const char *const data_mime_type,
84 const char *data, 200 const char *const data,
85 size_t data_size) 201 const size_t data_size)
86{ 202{
87 if ((format != EXTRACTOR_METAFORMAT_UTF8) && 203#define info ((struct GNUNET_SEARCH_MetadataPrinterInfo *) cls)
88 (format != EXTRACTOR_METAFORMAT_C_STRING)) 204 if ((format != EXTRACTOR_METAFORMAT_UTF8 &&
205 format != EXTRACTOR_METAFORMAT_C_STRING) ||
206 type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
89 return 0; 207 return 0;
90 if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME) 208 info->counter++;
209 if ((info->flags & METADATA_PRINTER_FLAG_HAVE_TYPE) && type != info->type)
91 return 0; 210 return 0;
211
212 const char *cursor = meta_format_string;
213 const char *next_spec = strchr(cursor, '%');
214 const char *next_esc = strchr(cursor, '\\');
215
216parse_format:
217
218 /* If an escape sequence exists before the next format specifier... */
219 if (next_esc && (!next_spec || next_esc < next_spec))
220 {
221 if (next_esc > cursor)
222 fwrite (cursor, 1, next_esc - cursor, stdout);
223
224 cursor = print_escape_sequence (next_esc);
225 next_esc = strchr(cursor, '\\');
226 goto parse_format;
227 }
228
229 /* If a format specifier exists before the next escape sequence... */
230 if (next_spec && (!next_esc || next_spec < next_esc))
231 {
232 if (next_spec > cursor)
233 fwrite (cursor, 1, next_spec - cursor, stdout);
234
235 switch (*++next_spec)
236 {
237 case '%': putchar('%'); break;
238 case 'i': printf ("%d", type); break;
239 case 'l': printf ("%lu", (long unsigned int) data_size); break;
240 case 'n': printf ("%u", info->counter); break;
241 case 'p': printf ("%s", data); break;
92#if HAVE_LIBEXTRACTOR 242#if HAVE_LIBEXTRACTOR
93 printf ("\t%20s: %s\n", 243 case 't':
94 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, 244 printf ("%s",
95 EXTRACTOR_metatype_to_string (type)), 245 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
96 data); 246 EXTRACTOR_metatype_to_string (type)));
97#else 247 break;
98 printf ("\t%20d: %s\n", type, data);
99#endif 248#endif
100 return 0; 249 case 'w': printf ("%s", plugin_name); break;
250 case '\0': putchar('%'); return 0;
251 default: printf ("%%%c", *next_spec); break;
252 }
253 cursor = next_spec + 1;
254 next_spec = strchr(cursor, '%');
255 goto parse_format;
256 }
257
258 if (*cursor)
259 printf ("%s", cursor);
260
261 return info->flags & METADATA_PRINTER_FLAG_ONE_RUN;
262#undef info
263}
264
265
266/**
267 * Print a search result according to the current formats
268 *
269 * @param filename the filename for this result
270 * @param uri the `struct GNUNET_FS_Uri` this result refers to
271 * @param metadata the `struct GNUNET_CONTAINER_MetaData` associated with this
272 result
273 * @param resultnum the result number
274 * @param is_directory GNUNET_YES if this is a directory, otherwise GNUNET_NO
275 * @author madmurphy
276 */
277static void
278print_search_result (const char *const filename,
279 const struct GNUNET_FS_Uri *const uri,
280 const struct GNUNET_CONTAINER_MetaData *const metadata,
281 const unsigned int resultnum,
282 const int is_directory)
283{
284
285 const char *cursor = GNUNET_YES == is_directory ?
286 dir_format_string
287 : format_string;
288
289 const char *next_spec = strchr(cursor, '%');
290 const char *next_esc = strchr(cursor, '\\');
291 char *placeholder;
292 struct GNUNET_SEARCH_MetadataPrinterInfo info;
293
294parse_format:
295 /* If an escape sequence exists before the next format specifier... */
296 if (next_esc && (!next_spec || next_esc < next_spec))
297 {
298 if (next_esc > cursor)
299 fwrite (cursor, 1, next_esc - cursor, stdout);
300
301 cursor = print_escape_sequence (next_esc);
302 next_esc = strchr(cursor, '\\');
303 goto parse_format;
304 }
305
306 /* If a format specifier exists before the next escape sequence... */
307 if (next_spec && (!next_esc || next_spec < next_esc))
308 {
309 if (next_spec > cursor)
310 fwrite (cursor, 1, next_spec - cursor, stdout);
311
312 switch (*++next_spec)
313 {
314 /* All metadata fields */
315 case 'a':
316 info.flags = METADATA_PRINTER_FLAG_NONE;
317
318iterate_meta:
319 info.counter = 0;
320 GNUNET_CONTAINER_meta_data_iterate (metadata, &item_printer, &info);
321 break;
322 /* File's name */
323 case 'f':
324 if (GNUNET_YES == is_directory)
325 {
326 printf ("%s%s", filename, GNUNET_FS_DIRECTORY_EXT);
327 break;
328 }
329 printf ("%s", filename);
330 break;
331 /* Only the first metadata field */
332 case 'j':
333 info.flags = METADATA_PRINTER_FLAG_ONE_RUN;
334 goto iterate_meta;
335 /* File name's length */
336 case 'l':
337 printf ("%lu",
338 (long unsigned int) ( GNUNET_YES == is_directory ?
339 strlen(filename) +
340 (sizeof(GNUNET_FS_DIRECTORY_EXT) - 1)
341 :
342 strlen(filename)));
343 break;
344 /* File's mime type */
345 case 'm':
346 if (GNUNET_YES == is_directory)
347 {
348 printf ("%s", GNUNET_FS_DIRECTORY_MIME);
349 break;
350 }
351 placeholder = GNUNET_CONTAINER_meta_data_get_by_type (
352 metadata,
353 EXTRACTOR_METATYPE_MIMETYPE);
354 printf ("%s", placeholder ? placeholder : GENERIC_FILE_MIMETYPE);
355 GNUNET_free (placeholder);
356 break;
357 /* Result number */
358 case 'n': printf ("%u", resultnum); break;
359 /* File's size */
360 case 's':
361 printf ("%" PRIu64, GNUNET_FS_uri_chk_get_file_size (uri));
362 break;
363 /* File's URI */
364 case 'u':
365 placeholder = GNUNET_FS_uri_to_string (uri);
366 printf ("%s", placeholder);
367 GNUNET_free (placeholder);
368 break;
369
370 /* We can add as many cases as we want here... */
371
372 /* Handle `%123#a` and `%123#j` (e.g. `%5#j` is a book title) */
373 case '0': case '1': case '2': case '3': case '4':
374 case '5': case '6': case '7': case '8': case '9':
375 cursor = next_spec;
376 info.type = *cursor - 48;
377 while (isdigit(*++cursor) && info.type < (INT_MAX - *cursor + 48) / 10)
378 info.type = info.type * 10 + *cursor - 48;
379 if (info.type == 0 || *cursor != '#')
380 goto not_a_specifier;
381 switch (*++cursor)
382 {
383 /* All metadata fields of type `info.type` */
384 case 'a':
385 next_spec = cursor;
386 info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE;
387 goto iterate_meta;
388
389 /* Only the first metadata field of type `info.type` */
390 case 'j':
391 next_spec = cursor;
392 info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE |
393 METADATA_PRINTER_FLAG_ONE_RUN;
394 goto iterate_meta;
395 }
396 goto not_a_specifier;
397
398 /* All other cases */
399 case '%': putchar('%'); break;
400 case '\0': putchar('%'); return;
401
402not_a_specifier:
403 default: printf ("%%%c", *next_spec); break;
404 }
405 cursor = next_spec + 1;
406 next_spec = strchr(cursor, '%');
407 goto parse_format;
408 }
409
410 if (*cursor)
411 printf ("%s", cursor);
101} 412}
102 413
103 414
104static void 415static void
105clean_task (void *cls) 416clean_task (void *const cls)
106{ 417{
107 size_t dsize; 418 size_t dsize;
108 void *ddata; 419 void *ddata;
@@ -126,9 +437,10 @@ clean_task (void *cls)
126 GNUNET_DISK_PERM_USER_READ 437 GNUNET_DISK_PERM_USER_READ
127 | GNUNET_DISK_PERM_USER_WRITE)) 438 | GNUNET_DISK_PERM_USER_WRITE))
128 { 439 {
129 fprintf (stderr, 440 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
130 _ ("Failed to write directory with search results to `%s'\n"), 441 _ ("Failed to write directory with search results to "
131 output_filename); 442 "`%s'\n"),
443 output_filename);
132 } 444 }
133 GNUNET_free (ddata); 445 GNUNET_free (ddata);
134 GNUNET_free (output_filename); 446 GNUNET_free (output_filename);
@@ -149,11 +461,11 @@ clean_task (void *cls)
149 * field in the GNUNET_FS_ProgressInfo struct. 461 * field in the GNUNET_FS_ProgressInfo struct.
150 */ 462 */
151static void * 463static void *
152progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info) 464progress_cb (void *const cls,
465 const struct GNUNET_FS_ProgressInfo *const info)
153{ 466{
154 static unsigned int cnt; 467 static unsigned int cnt;
155 int is_directory; 468 int is_directory;
156 char *uri;
157 char *filename; 469 char *filename;
158 470
159 switch (info->status) 471 switch (info->status)
@@ -162,13 +474,17 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
162 break; 474 break;
163 475
164 case GNUNET_FS_STATUS_SEARCH_RESULT: 476 case GNUNET_FS_STATUS_SEARCH_RESULT:
477 if (silent_mode)
478 break;
479
165 if (db != NULL) 480 if (db != NULL)
166 GNUNET_FS_directory_builder_add (db, 481 GNUNET_FS_directory_builder_add (
167 info->value.search.specifics.result.uri, 482 db,
168 info->value.search.specifics.result.meta, 483 info->value.search.specifics.result.uri,
169 NULL); 484 info->value.search.specifics.result.meta,
170 uri = GNUNET_FS_uri_to_string (info->value.search.specifics.result.uri); 485 NULL);
171 printf ("#%u:\n", ++cnt); 486
487 cnt++;
172 filename = GNUNET_CONTAINER_meta_data_get_by_type ( 488 filename = GNUNET_CONTAINER_meta_data_get_by_type (
173 info->value.search.specifics.result.meta, 489 info->value.search.specifics.result.meta,
174 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME); 490 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
@@ -179,45 +495,37 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
179 while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1])) 495 while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1]))
180 filename[strlen (filename) - 1] = '\0'; 496 filename[strlen (filename) - 1] = '\0';
181 GNUNET_DISK_filename_canonicalize (filename); 497 GNUNET_DISK_filename_canonicalize (filename);
182 if (GNUNET_YES == is_directory)
183 printf ("gnunet-download -o \"%s%s\" -R %s\n",
184 filename,
185 GNUNET_FS_DIRECTORY_EXT,
186 uri);
187 else
188 printf ("gnunet-download -o \"%s\" %s\n", filename, uri);
189 } 498 }
190 else if (GNUNET_YES == is_directory) 499 print_search_result ( filename ?
191 printf ("gnunet-download -o \"collection%s\" -R %s\n", 500 filename
192 GNUNET_FS_DIRECTORY_EXT, 501 : is_directory ?
193 uri); 502 GENERIC_DIRECTORY_NAME
194 else 503 :
195 printf ("gnunet-download %s\n", uri); 504 GENERIC_FILE_NAME,
196 if (verbose) 505 info->value.search.specifics.result.uri,
197 GNUNET_CONTAINER_meta_data_iterate (info->value.search.specifics.result 506 info->value.search.specifics.result.meta,
198 .meta, 507 cnt,
199 &item_printer, 508 is_directory);
200 NULL);
201 printf ("\n");
202 fflush (stdout); 509 fflush (stdout);
203 GNUNET_free (filename); 510 GNUNET_free (filename);
204 GNUNET_free (uri);
205 results++; 511 results++;
206 if ((results_limit > 0) && (results >= results_limit)) 512 if ((results_limit > 0) && (results >= results_limit))
513 {
207 GNUNET_SCHEDULER_shutdown (); 514 GNUNET_SCHEDULER_shutdown ();
515 /* otherwise the function might keep printing results for a while... */
516 silent_mode = GNUNET_YES;
517 }
208 break; 518 break;
209 519
210 case GNUNET_FS_STATUS_SEARCH_UPDATE: 520 case GNUNET_FS_STATUS_SEARCH_UPDATE:
211 break;
212
213 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED: 521 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
214 /* ignore */ 522 /* ignore */
215 break; 523 break;
216 524
217 case GNUNET_FS_STATUS_SEARCH_ERROR: 525 case GNUNET_FS_STATUS_SEARCH_ERROR:
218 fprintf (stderr, 526 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
219 _ ("Error searching: %s.\n"), 527 _ ("Error searching: %s.\n"),
220 info->value.search.specifics.error.message); 528 info->value.search.specifics.error.message);
221 GNUNET_SCHEDULER_shutdown (); 529 GNUNET_SCHEDULER_shutdown ();
222 break; 530 break;
223 531
@@ -226,7 +534,9 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
226 break; 534 break;
227 535
228 default: 536 default:
229 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status); 537 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
538 _ ("Unexpected status: %d\n"),
539 info->status);
230 break; 540 break;
231 } 541 }
232 return NULL; 542 return NULL;
@@ -234,7 +544,7 @@ progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
234 544
235 545
236static void 546static void
237shutdown_task (void *cls) 547shutdown_task (void *const cls)
238{ 548{
239 if (sc != NULL) 549 if (sc != NULL)
240 { 550 {
@@ -245,9 +555,10 @@ shutdown_task (void *cls)
245 555
246 556
247static void 557static void
248timeout_task (void *cls) 558timeout_task (void *const cls)
249{ 559{
250 tt = NULL; 560 tt = NULL;
561 silent_mode = GNUNET_YES;
251 GNUNET_SCHEDULER_shutdown (); 562 GNUNET_SCHEDULER_shutdown ();
252} 563}
253 564
@@ -258,18 +569,47 @@ timeout_task (void *cls)
258 * @param cls closure 569 * @param cls closure
259 * @param args remaining command-line arguments 570 * @param args remaining command-line arguments
260 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 571 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
261 * @param c configuration 572 * @param cfgarg configuration
262 */ 573 */
263static void 574static void
264run (void *cls, 575run (void *const cls,
265 char *const *args, 576 char *const *const args,
266 const char *cfgfile, 577 const char *const cfgfile,
267 const struct GNUNET_CONFIGURATION_Handle *c) 578 const struct GNUNET_CONFIGURATION_Handle *const cfgarg)
268{ 579{
269 struct GNUNET_FS_Uri *uri; 580 struct GNUNET_FS_Uri *uri;
270 unsigned int argc; 581 unsigned int argc;
271 enum GNUNET_FS_SearchOptions options; 582 enum GNUNET_FS_SearchOptions options;
272 583
584 if (silent_mode && bookmark_only)
585 {
586 fprintf (stderr,
587 _ ("Conflicting options --bookmark-only and --silent.\n"));
588 ret = 1;
589 return;
590 }
591 if (bookmark_only && output_filename)
592 {
593 fprintf (stderr,
594 _ ("Conflicting options --bookmark-only and --output.\n"));
595 ret = 1;
596 return;
597 }
598 if (silent_mode && !output_filename)
599 {
600 fprintf (stderr, _ ("An output file is mandatory for silent mode.\n"));
601 ret = 1;
602 return;
603 }
604 if (NULL == dir_format_string)
605 dir_format_string = format_string ? format_string
606 : verbose ? VERB_DEFAULT_DIR_FORMAT
607 : DEFAULT_DIR_FORMAT;
608 if (NULL == format_string)
609 format_string = verbose ? VERB_DEFAULT_FILE_FORMAT
610 : DEFAULT_FILE_FORMAT;
611 if (NULL == meta_format_string)
612 meta_format_string = DEFAULT_META_FORMAT;
273 argc = 0; 613 argc = 0;
274 while (NULL != args[argc]) 614 while (NULL != args[argc])
275 argc++; 615 argc++;
@@ -282,7 +622,27 @@ run (void *cls,
282 ret = 1; 622 ret = 1;
283 return; 623 return;
284 } 624 }
285 cfg = c; 625 if (!GNUNET_FS_uri_test_ksk (uri) && !GNUNET_FS_uri_test_sks (uri))
626 {
627 fprintf (stderr,
628 "%s",
629 _ ("Invalid URI. Valid URIs for searching are keyword query "
630 "URIs\n(\"gnunet://fs/ksk/...\") and namespace content URIs "
631 "(\"gnunet://fs/sks/...\").\n"));
632 GNUNET_FS_uri_destroy (uri);
633 ret = 1;
634 return;
635 }
636 if (bookmark_only)
637 {
638 char * bmstr = GNUNET_FS_uri_to_string (uri);
639 printf ("%s\n", bmstr);
640 GNUNET_free (bmstr);
641 GNUNET_FS_uri_destroy (uri);
642 ret = 0;
643 return;
644 }
645 cfg = cfgarg;
286 ctx = GNUNET_FS_start (cfg, 646 ctx = GNUNET_FS_start (cfg,
287 "gnunet-search", 647 "gnunet-search",
288 &progress_cb, 648 &progress_cb,
@@ -291,7 +651,7 @@ run (void *cls,
291 GNUNET_FS_OPTIONS_END); 651 GNUNET_FS_OPTIONS_END);
292 if (NULL == ctx) 652 if (NULL == ctx)
293 { 653 {
294 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS"); 654 fprintf (stderr, _ ("Could not initialize the `%s` subsystem.\n"), "FS");
295 GNUNET_FS_uri_destroy (uri); 655 GNUNET_FS_uri_destroy (uri);
296 ret = 1; 656 ret = 1;
297 return; 657 return;
@@ -321,18 +681,74 @@ run (void *cls,
321 * 681 *
322 * @param argc number of arguments from the command line 682 * @param argc number of arguments from the command line
323 * @param argv command line arguments 683 * @param argv command line arguments
324 * @return 0 ok, 1 on error 684 * @return 0 ok, an error number on error
325 */ 685 */
326int 686int
327main (int argc, char *const *argv) 687main (int argc, char *const *argv)
328{ 688{
329 struct GNUNET_GETOPT_CommandLineOption options[] = 689 struct GNUNET_GETOPT_CommandLineOption options[] =
330 { GNUNET_GETOPT_option_uint ('a', 690 { GNUNET_GETOPT_option_uint (
331 "anonymity", 691 'a',
332 "LEVEL", 692 "anonymity",
333 gettext_noop ( 693 "LEVEL",
334 "set the desired LEVEL of receiver-anonymity"), 694 gettext_noop ("set the desired LEVEL of receiver-anonymity (default: "
335 &anonymity), 695 "1)"),
696 &anonymity),
697 GNUNET_GETOPT_option_flag (
698 'b',
699 "bookmark-only",
700 gettext_noop ("do not search, print only the URI that points to this "
701 "search"),
702 &bookmark_only),
703 GNUNET_GETOPT_option_string (
704 'F',
705 "dir-printf",
706 "FORMAT",
707 gettext_noop ("write search results for directories according to "
708 "FORMAT; the format specifiers supported here are "
709 "identical to those supported in the --printf argument "
710 "(please refer to it for more information); if missing, "
711 "--dir-printf defaults to --printf; if --printf is "
712 "missing too --dir-printf defaults to `"
713 HELP_DEFAULT_DIR_FORMAT "`"),
714 &dir_format_string),
715 GNUNET_GETOPT_option_string (
716 'f',
717 "printf",
718 "FORMAT",
719 gettext_noop ("write search results according to FORMAT, where %a is "
720 "the complete list of all the printable metadata "
721 "available (each member will be displayed according to "
722 "the --iter-printf argument) - use %j for printing only "
723 "one field - %f is the file's name, %l is the file name's "
724 "length, %m is the file's mime type, %n is the search "
725 "result number, %s is the file's size in bytes and %u is "
726 "the file's URI; the %a and %j specifiers optionally "
727 "support metatype filtering via hash sign (e.g. `%5#j` "
728 "prints a book title, if present - see libextractor's "
729 "metatypes for the complete list of numerical "
730 "identifiers); if missing, --printf defaults to `"
731 HELP_DEFAULT_FILE_FORMAT "`"),
732 &format_string),
733 GNUNET_GETOPT_option_string (
734 'i',
735 "iter-printf",
736 "FORMAT",
737 gettext_noop ("when the %a or %j format specifiers appear in --printf "
738 "or --dir-printf, list each metadata property according "
739 "to FORMAT, where %p is the property's content, %l is the "
740 "content's length in bytes" HELP_EXTRACTOR_TEXTADD ", %i "
741 "is the property type's unique identifier, %n is the "
742 "property number and %w is the name of the plugin that "
743 "provided the information; if missing, --iter-printf "
744 "defaults to `" HELP_DEFAULT_META_FORMAT "`"),
745 &meta_format_string),
746 GNUNET_GETOPT_option_uint ('N',
747 "results",
748 "VALUE",
749 gettext_noop ("automatically terminate search "
750 "after VALUE results are found"),
751 &results_limit),
336 GNUNET_GETOPT_option_flag ( 752 GNUNET_GETOPT_option_flag (
337 'n', 753 'n',
338 "no-network", 754 "no-network",
@@ -341,39 +757,55 @@ main (int argc, char *const *argv)
341 GNUNET_GETOPT_option_string ( 757 GNUNET_GETOPT_option_string (
342 'o', 758 'o',
343 "output", 759 "output",
344 "PREFIX", 760 "FILENAME",
345 gettext_noop ("write search results to file starting with PREFIX"), 761 gettext_noop ("create a GNUnet directory with search results at "
762 "FILENAME (e.g. `gnunet-search --output=commons"
763 GNUNET_FS_DIRECTORY_EXT " commons`)"),
346 &output_filename), 764 &output_filename),
765 GNUNET_GETOPT_option_flag (
766 's',
767 "silent",
768 gettext_noop ("silent mode (requires the --output argument)"),
769 &silent_mode),
347 GNUNET_GETOPT_option_relative_time ( 770 GNUNET_GETOPT_option_relative_time (
348 't', 771 't',
349 "timeout", 772 "timeout",
350 "DELAY", 773 "DELAY",
351 gettext_noop ("automatically terminate search after DELAY"), 774 gettext_noop ("automatically terminate search after DELAY (in number of "
775 "microseconds - 1 second = 1000000); if 0 or omitted it "
776 "means to wait for CTRL-C"),
352 &timeout), 777 &timeout),
353 GNUNET_GETOPT_option_verbose (&verbose), 778 GNUNET_GETOPT_option_increment_uint (
354 GNUNET_GETOPT_option_uint ('N', 779 'V',
355 "results", 780 "verbose",
356 "VALUE", 781 gettext_noop ("be verbose (append \"%a\\n\" to the default --printf and "
357 gettext_noop ("automatically terminate search " 782 "--dir-printf arguments - ignored when these are provided "
358 "after VALUE results are found"), 783 "by the user)"),
359 &results_limit), 784 &verbose),
360 GNUNET_GETOPT_OPTION_END }; 785 GNUNET_GETOPT_OPTION_END };
361 786
362 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) 787 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
363 return 2; 788 return 2;
364 789
365 ret = 790 if (GNUNET_SYSERR ==
366 (GNUNET_OK == 791 GNUNET_PROGRAM_run (argc,
367 GNUNET_PROGRAM_run (argc, 792 argv,
368 argv, 793 "gnunet-search [OPTIONS] KEYWORD1 KEYWORD2 ...",
369 "gnunet-search [OPTIONS] KEYWORD", 794 gettext_noop ("Search for files that have been "
370 gettext_noop ( 795 "published on GNUnet\n\nKeywords "
371 "Search GNUnet for files that were published on GNUnet"), 796 "should start with a plus sign to "
797 "indicate that they are required -\n"
798 "e.g. `gnunet-search commons gpl` "
799 "searches for files that match "
800 "*either*\n\"commons\" or \"gpl\", "
801 "whereas `gnunet-search +commons "
802 "+gpl` searches for files\nthat match "
803 "*both* \"commons\" and \"gpl\".\n"),
372 options, 804 options,
373 &run, 805 &run,
374 NULL)) 806 NULL))
375 ? ret 807 ret = 1;
376 : 1; 808
377 GNUNET_free_nz ((void *) argv); 809 GNUNET_free_nz ((void *) argv);
378 return ret; 810 return ret;
379} 811}