aboutsummaryrefslogtreecommitdiff
path: root/src/fs/gnunet-search.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fs/gnunet-search.c')
-rw-r--r--src/fs/gnunet-search.c793
1 files changed, 0 insertions, 793 deletions
diff --git a/src/fs/gnunet-search.c b/src/fs/gnunet-search.c
deleted file mode 100644
index 7e2e4d2a6..000000000
--- a/src/fs/gnunet-search.c
+++ /dev/null
@@ -1,793 +0,0 @@
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 <ctype.h>
30#include <inttypes.h>
31#include <limits.h>
32#include "platform.h"
33#include "gnunet_fs_service.h"
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"
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
78static int ret;
79
80static const struct GNUNET_CONFIGURATION_Handle *cfg;
81
82static struct GNUNET_FS_Handle *ctx;
83
84static struct GNUNET_FS_SearchContext *sc;
85
86static char *output_filename;
87
88static char *format_string;
89
90static char *dir_format_string;
91
92static char *meta_format_string;
93
94static struct GNUNET_FS_DirectoryBuilder *db;
95
96static unsigned int anonymity = 1;
97
98/**
99 * Timeout for the search, 0 means to wait for CTRL-C.
100 */
101static struct GNUNET_TIME_Relative timeout;
102
103static unsigned int results_limit;
104
105static unsigned int results;
106
107static unsigned int verbose;
108
109static int bookmark_only;
110
111static int local_only;
112
113static int silent_mode;
114
115static struct GNUNET_SCHEDULER_Task *tt;
116
117
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/**
178 * Type of a function that libextractor calls for each
179 * meta data item found.
180 *
181 * @param cls closure (user-defined, used for the iteration info)
182 * @param plugin_name name of the plugin that produced this value;
183 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
184 * used in the main libextractor library and yielding
185 * meta data).
186 * @param type libextractor-type describing the meta data
187 * @param format basic format information about data
188 * @param data_mime_type mime-type of data (not of the original file);
189 * can be NULL (if mime-type is not known)
190 * @param data actual meta-data found
191 * @param data_size number of bytes in @a data
192 * @return 0 to continue extracting, 1 to abort
193 */
194static int
195item_printer (void *const cls,
196 const char *const plugin_name,
197 const enum EXTRACTOR_MetaType type,
198 const enum EXTRACTOR_MetaFormat format,
199 const char *const data_mime_type,
200 const char *const data,
201 const size_t data_size)
202{
203#define info ((struct GNUNET_SEARCH_MetadataPrinterInfo *) cls)
204 if ((format != EXTRACTOR_METAFORMAT_UTF8 &&
205 format != EXTRACTOR_METAFORMAT_C_STRING) ||
206 type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
207 return 0;
208 info->counter++;
209 if ((info->flags & METADATA_PRINTER_FLAG_HAVE_TYPE) && type != info->type)
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;
242#if HAVE_LIBEXTRACTOR
243 case 't':
244 printf ("%s",
245 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
246 EXTRACTOR_metatype_to_string (type)));
247 break;
248#endif
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);
412}
413
414
415static void
416clean_task (void *const cls)
417{
418 size_t dsize;
419 void *ddata;
420
421 GNUNET_FS_stop (ctx);
422 ctx = NULL;
423 if (output_filename == NULL)
424 return;
425 if (GNUNET_OK !=
426 GNUNET_FS_directory_builder_finish (db, &dsize, &ddata))
427 {
428 GNUNET_break (0);
429 GNUNET_free (output_filename);
430 return;
431 }
432 (void) GNUNET_DISK_directory_remove (output_filename);
433 if (GNUNET_OK !=
434 GNUNET_DISK_fn_write (output_filename,
435 ddata,
436 dsize,
437 GNUNET_DISK_PERM_USER_READ
438 | GNUNET_DISK_PERM_USER_WRITE))
439 {
440 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
441 _ ("Failed to write directory with search results to "
442 "`%s'\n"),
443 output_filename);
444 }
445 GNUNET_free (ddata);
446 GNUNET_free (output_filename);
447}
448
449
450/**
451 * Called by FS client to give information about the progress of an
452 * operation.
453 *
454 * @param cls closure
455 * @param info details about the event, specifying the event type
456 * and various bits about the event
457 * @return client-context (for the next progress call
458 * for this operation; should be set to NULL for
459 * SUSPEND and STOPPED events). The value returned
460 * will be passed to future callbacks in the respective
461 * field in the GNUNET_FS_ProgressInfo struct.
462 */
463static void *
464progress_cb (void *const cls,
465 const struct GNUNET_FS_ProgressInfo *const info)
466{
467 static unsigned int cnt;
468 int is_directory;
469 char *filename;
470
471 switch (info->status)
472 {
473 case GNUNET_FS_STATUS_SEARCH_START:
474 break;
475
476 case GNUNET_FS_STATUS_SEARCH_RESULT:
477 if (silent_mode)
478 break;
479
480 if (db != NULL)
481 GNUNET_FS_directory_builder_add (
482 db,
483 info->value.search.specifics.result.uri,
484 info->value.search.specifics.result.meta,
485 NULL);
486
487 cnt++;
488 filename = GNUNET_CONTAINER_meta_data_get_by_type (
489 info->value.search.specifics.result.meta,
490 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
491 is_directory = GNUNET_FS_meta_data_test_for_directory (
492 info->value.search.specifics.result.meta);
493 if (NULL != filename)
494 {
495 while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1]))
496 filename[strlen (filename) - 1] = '\0';
497 GNUNET_DISK_filename_canonicalize (filename);
498 }
499 print_search_result ( filename ?
500 filename
501 : is_directory ?
502 GENERIC_DIRECTORY_NAME
503 :
504 GENERIC_FILE_NAME,
505 info->value.search.specifics.result.uri,
506 info->value.search.specifics.result.meta,
507 cnt,
508 is_directory);
509 fflush (stdout);
510 GNUNET_free (filename);
511 results++;
512 if ((results_limit > 0) && (results >= results_limit))
513 {
514 GNUNET_SCHEDULER_shutdown ();
515 /* otherwise the function might keep printing results for a while... */
516 silent_mode = GNUNET_YES;
517 }
518 break;
519
520 case GNUNET_FS_STATUS_SEARCH_UPDATE:
521 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
522 /* ignore */
523 break;
524
525 case GNUNET_FS_STATUS_SEARCH_ERROR:
526 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
527 _ ("Error searching: %s.\n"),
528 info->value.search.specifics.error.message);
529 GNUNET_SCHEDULER_shutdown ();
530 break;
531
532 case GNUNET_FS_STATUS_SEARCH_STOPPED:
533 GNUNET_SCHEDULER_add_now (&clean_task, NULL);
534 break;
535
536 default:
537 GNUNET_SEARCH_log(GNUNET_ERROR_TYPE_ERROR,
538 _ ("Unexpected status: %d\n"),
539 info->status);
540 break;
541 }
542 return NULL;
543}
544
545
546static void
547shutdown_task (void *const cls)
548{
549 if (sc != NULL)
550 {
551 GNUNET_FS_search_stop (sc);
552 sc = NULL;
553 }
554}
555
556
557static void
558timeout_task (void *const cls)
559{
560 tt = NULL;
561 silent_mode = GNUNET_YES;
562 GNUNET_SCHEDULER_shutdown ();
563}
564
565
566/**
567 * Main function that will be run by the scheduler.
568 *
569 * @param cls closure
570 * @param args remaining command-line arguments
571 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
572 * @param cfgarg configuration
573 */
574static void
575run (void *const cls,
576 char *const *const args,
577 const char *const cfgfile,
578 const struct GNUNET_CONFIGURATION_Handle *const cfgarg)
579{
580 struct GNUNET_FS_Uri *uri;
581 unsigned int argc;
582 enum GNUNET_FS_SearchOptions options;
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;
613 argc = 0;
614 while (NULL != args[argc])
615 argc++;
616 uri = GNUNET_FS_uri_ksk_create_from_args (argc, (const char **) args);
617 if (NULL == uri)
618 {
619 fprintf (stderr,
620 "%s",
621 _ ("Could not create keyword URI from arguments.\n"));
622 ret = 1;
623 return;
624 }
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;
646 ctx = GNUNET_FS_start (cfg,
647 "gnunet-search",
648 &progress_cb,
649 NULL,
650 GNUNET_FS_FLAGS_NONE,
651 GNUNET_FS_OPTIONS_END);
652 if (NULL == ctx)
653 {
654 fprintf (stderr, _ ("Could not initialize the `%s` subsystem.\n"), "FS");
655 GNUNET_FS_uri_destroy (uri);
656 ret = 1;
657 return;
658 }
659 if (output_filename != NULL)
660 db = GNUNET_FS_directory_builder_create (NULL);
661 options = GNUNET_FS_SEARCH_OPTION_NONE;
662 if (local_only)
663 options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY;
664 sc = GNUNET_FS_search_start (ctx, uri, anonymity, options, NULL);
665 GNUNET_FS_uri_destroy (uri);
666 if (NULL == sc)
667 {
668 fprintf (stderr, "%s", _ ("Could not start searching.\n"));
669 GNUNET_FS_stop (ctx);
670 ret = 1;
671 return;
672 }
673 if (0 != timeout.rel_value_us)
674 tt = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, NULL);
675 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
676}
677
678
679/**
680 * The main function to search GNUnet.
681 *
682 * @param argc number of arguments from the command line
683 * @param argv command line arguments
684 * @return 0 ok, an error number on error
685 */
686int
687main (int argc, char *const *argv)
688{
689 struct GNUNET_GETOPT_CommandLineOption options[] =
690 { GNUNET_GETOPT_option_uint (
691 'a',
692 "anonymity",
693 "LEVEL",
694 gettext_noop ("set the desired LEVEL of receiver-anonymity (default: "
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; accepted placeholders are: %a, %f, %j, %l, %m, "
709 "%n, %s; defaults to the value of --printf when omitted "
710 "or to `" HELP_DEFAULT_DIR_FORMAT "` if --printf is "
711 "omitted too"),
712 &dir_format_string),
713 GNUNET_GETOPT_option_string (
714 'f',
715 "printf",
716 "FORMAT",
717 gettext_noop ("write search results according to FORMAT; accepted "
718 "placeholders are: %a, %f, %j, %l, %m, %n, %s; defaults "
719 "to `" HELP_DEFAULT_FILE_FORMAT "` when omitted"),
720 &format_string),
721 GNUNET_GETOPT_option_string (
722 'i',
723 "iter-printf",
724 "FORMAT",
725 gettext_noop ("when the %a or %j placeholders appear in --printf or "
726 "--dir-printf, list each metadata property according to "
727 "FORMAT; accepted placeholders are: %i, %l, %n, %p"
728 HELP_EXTRACTOR_TEXTADD ", %w; defaults to `"
729 HELP_DEFAULT_META_FORMAT "` when omitted"),
730 &meta_format_string),
731 GNUNET_GETOPT_option_uint ('N',
732 "results",
733 "VALUE",
734 gettext_noop ("automatically terminate search "
735 "after VALUE results are found"),
736 &results_limit),
737 GNUNET_GETOPT_option_flag (
738 'n',
739 "no-network",
740 gettext_noop ("only search the local peer (no P2P network search)"),
741 &local_only),
742 GNUNET_GETOPT_option_string (
743 'o',
744 "output",
745 "FILENAME",
746 gettext_noop ("create a GNUnet directory with search results at "
747 "FILENAME (e.g. `gnunet-search --output=commons"
748 GNUNET_FS_DIRECTORY_EXT " commons`)"),
749 &output_filename),
750 GNUNET_GETOPT_option_flag (
751 's',
752 "silent",
753 gettext_noop ("silent mode (requires the --output argument)"),
754 &silent_mode),
755 GNUNET_GETOPT_option_relative_time (
756 't',
757 "timeout",
758 "DELAY",
759 gettext_noop ("automatically terminate search after DELAY; the value "
760 "given must be a number followed by a space and a time "
761 "unit, for example \"500 ms\"; without a unit it defaults "
762 "to microseconds - 1000000 = 1 second; if 0 or omitted "
763 "it means to wait for CTRL-C"),
764 &timeout),
765 GNUNET_GETOPT_option_increment_uint (
766 'V',
767 "verbose",
768 gettext_noop ("be verbose (append \"%a\\n\" to the default --printf and "
769 "--dir-printf arguments - ignored when these are provided "
770 "by the user)"),
771 &verbose),
772 GNUNET_GETOPT_OPTION_END };
773
774 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
775 return 12;
776
777 if (GNUNET_SYSERR ==
778 GNUNET_PROGRAM_run (argc,
779 argv,
780 "gnunet-search [OPTIONS] KEYWORD1 KEYWORD2 ...",
781 gettext_noop ("Search for files that have been "
782 "published on GNUnet\n"),
783 options,
784 &run,
785 NULL))
786 ret = 1;
787
788 GNUNET_free_nz ((void *) argv);
789 return ret;
790}
791
792
793/* end of gnunet-search.c */