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