Imported Upstream version 0.19.7
[platform/upstream/gettext.git] / gettext-tools / src / urlget.c
1 /* Get the contents of an URL.
2    Copyright (C) 2001-2003, 2005-2010, 2012, 2015 Free Software
3    Foundation, Inc.
4    Written by Bruno Haible <haible@clisp.cons.org>, 2001.
5
6    This program is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19
20 #ifdef HAVE_CONFIG_H
21 # include "config.h"
22 #endif
23
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <stdbool.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <locale.h>
31 #include <unistd.h>
32
33 #include "closeout.h"
34 #include "error.h"
35 #include "error-progname.h"
36 #include "progname.h"
37 #include "relocatable.h"
38 #include "basename.h"
39 #include "full-write.h"
40 #include "execute.h"
41 #include "javaexec.h"
42 #include "binary-io.h"
43 #include "propername.h"
44 #include "gettext.h"
45
46 #define _(str) gettext (str)
47
48 #ifndef STDOUT_FILENO
49 # define STDOUT_FILENO 1
50 #endif
51
52
53 /* Only high-level toolkits, written in languages with exception handling,
54    have an URL datatype and operations to fetch an URL's contents.  Such
55    toolkits are Java (class java.net.URL), Qt (classes QUrl and QUrlOperator).
56    We use the Java toolkit.
57    Note that this program doesn't handle redirection pages; programs which
58    wish to process HTML redirection tags need to include a HTML parser,
59    and only full-fledged browsers like w3m, lynx, links have have both
60    an URL fetcher (which covers at least the protocols "http", "ftp", "file")
61    and a HTML parser.  [Well, this is not true: libxml2 and Java (see
62    <http://java.sun.com/products/jfc/tsc/articles/bookmarks/>) also contain
63    HTML parsers.]  */
64
65
66 /* Whether to output something on standard error.
67    This is true by default, because the user should know why we are trying to
68    establish an internet connection.  Also, users get confused if a program
69    produces no output for more than 10 seconds for no apparent reason.  */
70 static bool verbose = true;
71
72 /* Long options.  */
73 static const struct option long_options[] =
74 {
75   { "help", no_argument, NULL, 'h' },
76   { "quiet", no_argument, NULL, 'q' },
77   { "silent", no_argument, NULL, 'q' },
78   { "version", no_argument, NULL, 'V' },
79   { NULL, 0, NULL, 0 }
80 };
81
82
83 /* Forward declaration of local functions.  */
84 static void usage (int status)
85 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
86      __attribute__ ((noreturn))
87 #endif
88 ;
89 static void fetch (const char *url, const char *file);
90
91
92 int
93 main (int argc, char *argv[])
94 {
95   int optchar;
96   bool do_help;
97   bool do_version;
98
99   /* Set program name for messages.  */
100   set_program_name (argv[0]);
101   error_print_progname = maybe_print_progname;
102
103 #ifdef HAVE_SETLOCALE
104   /* Set locale via LC_ALL.  */
105   setlocale (LC_ALL, "");
106 #endif
107
108   /* Set the text message domain.  */
109   bindtextdomain (PACKAGE, relocate (LOCALEDIR));
110   textdomain (PACKAGE);
111
112   /* Ensure that write errors on stdout are detected.  */
113   atexit (close_stdout);
114
115   /* Set default values for variables.  */
116   do_help = false;
117   do_version = false;
118
119   /* Parse command line options.  */
120   while ((optchar = getopt_long (argc, argv, "hqV", long_options, NULL)) != EOF)
121     switch (optchar)
122     {
123     case '\0':          /* Long option.  */
124       break;
125     case 'h':           /* --help */
126       do_help = true;
127       break;
128     case 'q':           /* --quiet / --silent */
129       verbose = false;
130       break;
131     case 'V':           /* --version */
132       do_version = true;
133       break;
134     default:
135       usage (EXIT_FAILURE);
136       /* NOTREACHED */
137     }
138
139   /* Version information requested.  */
140   if (do_version)
141     {
142       printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
143       /* xgettext: no-wrap */
144       printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
145 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
146 This is free software: you are free to change and redistribute it.\n\
147 There is NO WARRANTY, to the extent permitted by law.\n\
148 "),
149               "2001-2003, 2005-2009");
150       printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
151       exit (EXIT_SUCCESS);
152     }
153
154   /* Help is requested.  */
155   if (do_help)
156     usage (EXIT_SUCCESS);
157
158   /* Test argument count.  */
159   if (optind + 2 != argc)
160     error (EXIT_FAILURE, 0, _("expected two arguments"));
161
162   /* Fetch the contents.  */
163   fetch (argv[optind], argv[optind + 1]);
164
165   exit (EXIT_SUCCESS);
166 }
167
168 /* Display usage information and exit.  */
169 static void
170 usage (int status)
171 {
172   if (status != EXIT_SUCCESS)
173     fprintf (stderr, _("Try '%s --help' for more information.\n"),
174              program_name);
175   else
176     {
177       printf (_("\
178 Usage: %s [OPTION] URL FILE\n\
179 "), program_name);
180       printf ("\n");
181       /* xgettext: no-wrap */
182       printf (_("\
183 Fetches and outputs the contents of an URL.  If the URL cannot be accessed,\n\
184 the locally accessible FILE is used instead.\n\
185 "));
186       printf ("\n");
187       printf (_("\
188 Informative output:\n"));
189       printf (_("\
190   -h, --help                  display this help and exit\n"));
191       printf (_("\
192   -V, --version               output version information and exit\n"));
193       printf (_("\
194   -q, --quiet, --silent       suppress progress indicators\n"));
195       printf ("\n");
196       /* TRANSLATORS: The placeholder indicates the bug-reporting address
197          for this package.  Please add _another line_ saying
198          "Report translation bugs to <...>\n" with the address for translation
199          bugs (typically your translation team's web or email address).  */
200       fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
201              stdout);
202     }
203
204   exit (status);
205 }
206
207 /* Copy a file's contents to stdout.  */
208 static void
209 cat_file (const char *src_filename)
210 {
211   int src_fd;
212   char buf[4096];
213   const int buf_size = sizeof (buf);
214
215   src_fd = open (src_filename, O_RDONLY | O_BINARY);
216   if (src_fd < 0)
217     error (EXIT_FAILURE, errno, _("error while opening \"%s\" for reading"),
218            src_filename);
219
220   for (;;)
221     {
222       ssize_t n_read = read (src_fd, buf, buf_size);
223       if (n_read < 0)
224         {
225 #ifdef EINTR
226           if (errno == EINTR)
227             continue;
228 #endif
229           error (EXIT_FAILURE, errno, _("error reading \"%s\""), src_filename);
230         }
231       if (n_read == 0)
232         break;
233
234       if (full_write (STDOUT_FILENO, buf, n_read) < n_read)
235         error (EXIT_FAILURE, errno, _("error writing stdout"));
236     }
237
238   if (close (src_fd) < 0)
239     error (EXIT_FAILURE, errno, _("error after reading \"%s\""), src_filename);
240 }
241
242 /* Exit code of the Java program.  */
243 static int java_exitcode;
244
245 static bool
246 execute_it (const char *progname,
247             const char *prog_path, char **prog_argv,
248             void *private_data)
249 {
250   (void) private_data;
251
252   java_exitcode =
253     execute (progname, prog_path, prog_argv, true, true, false, false, true,
254              false, NULL);
255   /* Exit code 0 means success, 2 means timed out.  */
256   return !(java_exitcode == 0 || java_exitcode == 2);
257 }
258
259 /* Fetch the URL.  Upon error, use the FILE as fallback.  */
260 static void
261 fetch (const char *url, const char *file)
262 {
263   if (verbose)
264     {
265       fprintf (stderr, _("Retrieving %s..."), url);
266       fflush (stderr);
267     }
268
269 #if USEJAVA
270   /* First try: using Java.  */
271   {
272     const char *class_name = "gnu.gettext.GetURL";
273     const char *gettextjexedir;
274     const char *gettextjar;
275     const char *args[2];
276
277 # if USEJEXE
278     /* Make it possible to override the executable's location.  This is
279        necessary for running the testsuite before "make install".  */
280     gettextjexedir = getenv ("GETTEXTJEXEDIR");
281     if (gettextjexedir == NULL || gettextjexedir[0] == '\0')
282       gettextjexedir = relocate (GETTEXTJEXEDIR);
283 # else
284     gettextjexedir = NULL;
285 # endif
286
287     /* Make it possible to override the gettext.jar location.  This is
288        necessary for running the testsuite before "make install".  */
289     gettextjar = getenv ("GETTEXTJAR");
290     if (gettextjar == NULL || gettextjar[0] == '\0')
291       gettextjar = relocate (GETTEXTJAR);
292
293     /* Prepare arguments.  */
294     args[0] = url;
295     args[1] = NULL;
296
297     /* Fetch the URL's contents.  */
298     java_exitcode = 127;
299     if (!execute_java_class (class_name, &gettextjar, 1, true, gettextjexedir,
300                              args,
301                              false, true,
302                              execute_it, NULL))
303       {
304         if (verbose)
305           {
306             if (java_exitcode == 0)
307               fprintf (stderr, _(" done.\n"));
308             else if (java_exitcode == 2)
309               fprintf (stderr, _(" timed out.\n"));
310           }
311         return;
312       }
313   }
314 #endif
315
316   /* Second try: using "wget -q -O - -T 30 url".  */
317   {
318     static bool wget_tested;
319     static bool wget_present;
320
321     if (!wget_tested)
322       {
323         /* Test for presence of wget: "wget --version > /dev/null"  */
324         char *argv[3];
325         int exitstatus;
326
327         argv[0] = "wget";
328         argv[1] = "--version";
329         argv[2] = NULL;
330         exitstatus = execute ("wget", "wget", argv, false, false, true, true,
331                               true, false, NULL);
332         wget_present = (exitstatus == 0);
333         wget_tested = true;
334       }
335
336     if (wget_present)
337       {
338         char *argv[8];
339         int exitstatus;
340
341         argv[0] = "wget";
342         argv[1] = "-q";
343         argv[2] = "-O"; argv[3] = "-";
344         argv[4] = "-T"; argv[5] = "30";
345         argv[6] = (char *) url;
346         argv[7] = NULL;
347         exitstatus = execute ("wget", "wget", argv, true, false, false, false,
348                               true, false, NULL);
349         if (exitstatus != 127)
350           {
351             if (exitstatus != 0)
352               goto failed;
353             if (verbose)
354               fprintf (stderr, _(" done.\n"));
355             return;
356           }
357       }
358   }
359
360   /* Third try: using "lynx -source url".  */
361   {
362     static bool lynx_tested;
363     static bool lynx_present;
364
365     if (!lynx_tested)
366       {
367         /* Test for presence of lynx: "lynx --version > /dev/null"  */
368         char *argv[3];
369         int exitstatus;
370
371         argv[0] = "lynx";
372         argv[1] = "--version";
373         argv[2] = NULL;
374         exitstatus = execute ("lynx", "lynx", argv, false, false, true, true,
375                               true, false, NULL);
376         lynx_present = (exitstatus == 0);
377         lynx_tested = true;
378       }
379
380     if (lynx_present)
381       {
382         char *argv[4];
383         int exitstatus;
384
385         argv[0] = "lynx";
386         argv[1] = "-source";
387         argv[2] = (char *) url;
388         argv[3] = NULL;
389         exitstatus = execute ("lynx", "lynx", argv, true, false, false, false,
390                               true, false, NULL);
391         if (exitstatus != 127)
392           {
393             if (exitstatus != 0)
394               goto failed;
395             if (verbose)
396               fprintf (stderr, _(" done.\n"));
397             return;
398           }
399       }
400   }
401
402   /* Fourth try: using "curl --silent url".  */
403   {
404     static bool curl_tested;
405     static bool curl_present;
406
407     if (!curl_tested)
408       {
409         /* Test for presence of curl: "curl --version > /dev/null"  */
410         char *argv[3];
411         int exitstatus;
412
413         argv[0] = "curl";
414         argv[1] = "--version";
415         argv[2] = NULL;
416         exitstatus = execute ("curl", "curl", argv, false, false, true, true,
417                               true, false, NULL);
418         curl_present = (exitstatus == 0 || exitstatus == 2);
419         curl_tested = true;
420       }
421
422     if (curl_present)
423       {
424         char *argv[4];
425         int exitstatus;
426
427         argv[0] = "curl";
428         argv[1] = "--silent";
429         argv[2] = (char *) url;
430         argv[3] = NULL;
431         exitstatus = execute ("curl", "curl", argv, true, false, false, false,
432                               true, false, NULL);
433         if (exitstatus != 127)
434           {
435             if (exitstatus != 0)
436               goto failed;
437             if (verbose)
438               fprintf (stderr, _(" done.\n"));
439             return;
440           }
441       }
442   }
443
444  failed:
445   if (verbose)
446     fprintf (stderr, _(" failed.\n"));
447   /* Use the file as fallback.  */
448   cat_file (file);
449 }