reduce loop counters to more practical levels
[oweals/gnunet.git] / src / fs / gnunet-download.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file fs/gnunet-download.c
22  * @brief downloading for files on GNUnet
23  * @author Christian Grothoff
24  * @author Krista Bennett
25  * @author James Blackwell
26  * @author Igor Wronsky
27  */
28 #include "platform.h"
29 #include "gnunet_fs_service.h"
30
31 static int ret;
32
33 static unsigned int verbose;
34
35 static int delete_incomplete;
36
37 static const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39 static struct GNUNET_FS_Handle *ctx;
40
41 static struct GNUNET_FS_DownloadContext *dc;
42
43 static unsigned int anonymity = 1;
44
45 static unsigned int parallelism = 16;
46
47 static unsigned int request_parallelism = 4092;
48
49 static int do_recursive;
50
51 static char *filename;
52
53 static int local_only;
54
55
56 static void
57 cleanup_task (void *cls)
58 {
59   GNUNET_FS_stop (ctx);
60   ctx = NULL;
61 }
62
63
64 static void
65 shutdown_task (void *cls)
66 {
67   if (NULL != dc)
68   {
69     GNUNET_FS_download_stop (dc, delete_incomplete);
70     dc = NULL;
71   }
72 }
73
74
75 /**
76  * Display progress bar (if tty).
77  *
78  * @param x current position in the download
79  * @param n total size of the download
80  * @param w desired number of steps in the progress bar
81  */
82 static void
83 display_bar (unsigned long long x,
84              unsigned long long n,
85              unsigned int w)
86 {
87   char buf[w + 20];
88   unsigned int p;
89   unsigned int endeq;
90   float ratio_complete;
91
92 #if !WINDOWS
93   if (0 == isatty (1))
94     return;
95 #else
96   if (FILE_TYPE_CHAR != GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
97     return;
98 #endif
99   ratio_complete = x/(float)n;
100   endeq = ratio_complete * w;
101   GNUNET_snprintf (buf, sizeof (buf),
102                    "%3d%% [", (int)(ratio_complete*100) );
103   for (p=0; p<endeq; p++)
104     strcat (buf, "=");
105   for (p=endeq; p<w; p++)
106     strcat (buf, " ");
107   strcat (buf, "]\r");
108   printf ("%s", buf);
109   fflush(stdout);
110 }
111
112
113 /**
114  * Called by FS client to give information about the progress of an
115  * operation.
116  *
117  * @param cls closure
118  * @param info details about the event, specifying the event type
119  *        and various bits about the event
120  * @return client-context (for the next progress call
121  *         for this operation; should be set to NULL for
122  *         SUSPEND and STOPPED events).  The value returned
123  *         will be passed to future callbacks in the respective
124  *         field in the `struct GNUNET_FS_ProgressInfo`
125  */
126 static void *
127 progress_cb (void *cls,
128              const struct GNUNET_FS_ProgressInfo *info)
129 {
130   char *s;
131   const char *s2;
132   char *t;
133
134   switch (info->status)
135   {
136   case GNUNET_FS_STATUS_DOWNLOAD_START:
137     if (verbose > 1)
138       FPRINTF (stderr,
139                _("Starting download `%s'.\n"),
140                info->value.download.filename);
141     break;
142   case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
143     if (verbose)
144     {
145       s = GNUNET_strdup (GNUNET_STRINGS_relative_time_to_string (info->value.download.eta,
146                                                                  GNUNET_YES));
147       if (info->value.download.specifics.progress.block_download_duration.rel_value_us
148           == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
149         s2 = _("<unknown time>");
150       else
151         s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download.specifics.progress.block_download_duration,
152                                                      GNUNET_YES);
153       t = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed *
154                                           1000LL /
155                                           (info->value.download.
156                                            duration.rel_value_us + 1));
157       FPRINTF (stdout,
158                _("Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
159                info->value.download.filename,
160                (unsigned long long) info->value.download.completed,
161                (unsigned long long) info->value.download.size,
162                s,
163                t,
164                s2);
165       GNUNET_free (s);
166       GNUNET_free (t);
167     }
168     else
169     {
170       display_bar (info->value.download.completed,
171                    info->value.download.size,
172                    60);
173     }
174     break;
175   case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
176 #if !WINDOWS
177     if (0 != isatty (1))
178       fprintf (stdout, "\n");
179 #else
180     if (FILE_TYPE_CHAR ==
181         GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
182       fprintf (stdout, "\n");
183 #endif
184     FPRINTF (stderr, _("Error downloading: %s.\n"),
185              info->value.download.specifics.error.message);
186     GNUNET_SCHEDULER_shutdown ();
187     break;
188   case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
189     s = GNUNET_STRINGS_byte_size_fancy (info->value.download.completed * 1000 /
190                                         (info->value.download.
191                                          duration.rel_value_us + 1));
192 #if !WINDOWS
193     if (0 != isatty (1))
194       fprintf (stdout, "\n");
195 #else
196     if (FILE_TYPE_CHAR ==
197         GetFileType (GetStdHandle (STD_OUTPUT_HANDLE)))
198       fprintf (stdout, "\n");
199 #endif
200     FPRINTF (stdout,
201              _("Downloading `%s' done (%s/s).\n"),
202              info->value.download.filename, s);
203     GNUNET_free (s);
204     if (info->value.download.dc == dc)
205       GNUNET_SCHEDULER_shutdown ();
206     break;
207   case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
208     if (info->value.download.dc == dc)
209       GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
210     break;
211   case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
212   case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
213     break;
214   default:
215     FPRINTF (stderr,
216              _("Unexpected status: %d\n"),
217              info->status);
218     break;
219   }
220   return NULL;
221 }
222
223
224 /**
225  * Main function that will be run by the scheduler.
226  *
227  * @param cls closure
228  * @param args remaining command-line arguments
229  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
230  * @param c configuration
231  */
232 static void
233 run (void *cls,
234      char *const *args,
235      const char *cfgfile,
236      const struct GNUNET_CONFIGURATION_Handle *c)
237 {
238   struct GNUNET_FS_Uri *uri;
239   char *emsg;
240   enum GNUNET_FS_DownloadOptions options;
241
242   if (NULL == args[0])
243   {
244     FPRINTF (stderr,
245              "%s",
246              _("You need to specify a URI argument.\n"));
247     return;
248   }
249   uri = GNUNET_FS_uri_parse (args[0], &emsg);
250   if (NULL == uri)
251   {
252     FPRINTF (stderr,
253              _("Failed to parse URI: %s\n"),
254              emsg);
255     GNUNET_free (emsg);
256     ret = 1;
257     return;
258   }
259   if ( (! GNUNET_FS_uri_test_chk (uri)) &&
260        (! GNUNET_FS_uri_test_loc (uri)))
261   {
262     FPRINTF (stderr,
263              "%s",
264              _("Only CHK or LOC URIs supported.\n"));
265     ret = 1;
266     GNUNET_FS_uri_destroy (uri);
267     return;
268   }
269   if (NULL == filename)
270   {
271     FPRINTF (stderr,
272              "%s",
273              _("Target filename must be specified.\n"));
274     ret = 1;
275     GNUNET_FS_uri_destroy (uri);
276     return;
277   }
278   cfg = c;
279   ctx = GNUNET_FS_start (cfg,
280                          "gnunet-download",
281                          &progress_cb, NULL,
282                          GNUNET_FS_FLAGS_NONE,
283                          GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM,
284                          parallelism,
285                          GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
286                          request_parallelism,
287                          GNUNET_FS_OPTIONS_END);
288   if (NULL == ctx)
289   {
290     FPRINTF (stderr,
291              _("Could not initialize `%s' subsystem.\n"),
292              "FS");
293     GNUNET_FS_uri_destroy (uri);
294     ret = 1;
295     return;
296   }
297   options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
298   if (do_recursive)
299     options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
300   if (local_only)
301     options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
302   dc = GNUNET_FS_download_start (ctx,
303                                  uri,
304                                  NULL,
305                                  filename,
306                                  NULL,
307                                  0,
308                                  GNUNET_FS_uri_chk_get_file_size (uri),
309                                  anonymity,
310                                  options,
311                                  NULL,
312                                  NULL);
313   GNUNET_FS_uri_destroy (uri);
314   if (dc == NULL)
315   {
316     GNUNET_FS_stop (ctx);
317     ctx = NULL;
318     return;
319   }
320   GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
321                                  NULL);
322 }
323
324
325 /**
326  * The main function to download GNUnet.
327  *
328  * @param argc number of arguments from the command line
329  * @param argv command line arguments
330  * @return 0 ok, 1 on error
331  */
332 int
333 main (int argc, char *const *argv)
334 {
335   struct GNUNET_GETOPT_CommandLineOption options[] = {
336     GNUNET_GETOPT_option_uint ('a',
337                                "anonymity",
338                                "LEVEL",
339                                gettext_noop ("set the desired LEVEL of receiver-anonymity"),
340                                &anonymity),
341
342     GNUNET_GETOPT_option_flag ('D',
343                                "delete-incomplete",
344                                gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"),
345                                &delete_incomplete),
346
347     GNUNET_GETOPT_option_flag ('n',
348                                "no-network",
349                                gettext_noop ("only search the local peer (no P2P network search)"),
350                                &local_only), 
351     GNUNET_GETOPT_option_string ('o',
352                                  "output",
353                                  "FILENAME",
354                                  gettext_noop ("write the file to FILENAME"),
355                                  &filename),
356     GNUNET_GETOPT_option_uint ('p',
357                                "parallelism",
358                                "DOWNLOADS",
359                                gettext_noop ("set the maximum number of parallel downloads that is allowed"),
360                                &parallelism),
361     GNUNET_GETOPT_option_uint ('r',
362                                "request-parallelism",
363                                "REQUESTS",
364                                gettext_noop ("set the maximum number of parallel requests for blocks that is allowed"),
365                                &request_parallelism), 
366     GNUNET_GETOPT_option_flag ('R',
367                                "recursive",
368                                gettext_noop ("download a GNUnet directory recursively"),
369                                &do_recursive),
370     GNUNET_GETOPT_option_increment_uint ('V',
371                                          "verbose",
372                                          gettext_noop ("be verbose (print progress information)"),
373                                          &verbose), 
374     GNUNET_GETOPT_OPTION_END
375   };
376
377   if (GNUNET_OK !=
378       GNUNET_STRINGS_get_utf8_args (argc, argv,
379                                     &argc, &argv))
380     return 2;
381
382   ret = (GNUNET_OK ==
383          GNUNET_PROGRAM_run (argc, argv,
384                              "gnunet-download [OPTIONS] URI",
385                              gettext_noop
386                              ("Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
387                              options,
388                              &run, NULL)) ? ret : 1;
389   GNUNET_free ((void*) argv);
390   return ret;
391 }
392
393 /* end of gnunet-download.c */