1 /* Get the contents of an URL.
2 Copyright (C) 2001-2003, 2005-2006 Free Software Foundation, Inc.
3 Written by Bruno Haible <haible@clisp.cons.org>, 2001.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software Foundation,
17 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
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 "exit.h"
43 #include "binary-io.h"
44 #include "propername.h"
45 #include "gettext.h"
46
47 #define _(str) gettext (str)
48
49 #ifndef STDOUT_FILENO
50 # define STDOUT_FILENO 1
51 #endif
52
53
54 /* Only high-level toolkits, written in languages with exception handling,
55 have an URL datatype and operations to fetch an URL's contents. Such
56 toolkits are Java (class java.net.URL), Qt (classes QUrl and QUrlOperator).
57 We use the Java toolkit.
58 Note that this program doesn't handle redirection pages; programs which
59 wish to process HTML redirection tags need to include a HTML parser,
60 and only full-fledged browsers like w3m, lynx, links have have both
61 an URL fetcher (which covers at least the protocols "http", "ftp", "file")
62 and a HTML parser. */
63
64
65 /* Long options. */
66 static const struct option long_options[] =
67 {
68 { "help", no_argument, NULL, 'h' },
69 { "version", no_argument, NULL, 'V' },
70 { NULL, 0, NULL, 0 }
71 };
72
73
74 /* Forward declaration of local functions. */
75 static void usage (int status)
76 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
77 __attribute__ ((noreturn))
78 #endif
79 ;
80 static void fetch (const char *url, const char *file);
81
82
83 int
main(int argc,char * argv[])84 main (int argc, char *argv[])
85 {
86 int optchar;
87 bool do_help;
88 bool do_version;
89
90 /* Set program name for messages. */
91 set_program_name (argv[0]);
92 error_print_progname = maybe_print_progname;
93
94 #ifdef HAVE_SETLOCALE
95 /* Set locale via LC_ALL. */
96 setlocale (LC_ALL, "");
97 #endif
98
99 /* Set the text message domain. */
100 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
101 textdomain (PACKAGE);
102
103 /* Ensure that write errors on stdout are detected. */
104 atexit (close_stdout);
105
106 /* Set default values for variables. */
107 do_help = false;
108 do_version = false;
109
110 /* Parse command line options. */
111 while ((optchar = getopt_long (argc, argv, "hV", long_options, NULL)) != EOF)
112 switch (optchar)
113 {
114 case '\0': /* Long option. */
115 break;
116 case 'h':
117 do_help = true;
118 break;
119 case 'V':
120 do_version = true;
121 break;
122 default:
123 usage (EXIT_FAILURE);
124 /* NOTREACHED */
125 }
126
127 /* Version information requested. */
128 if (do_version)
129 {
130 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
131 /* xgettext: no-wrap */
132 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
133 This is free software; see the source for copying conditions. There is NO\n\
134 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
135 "),
136 "2001-2003");
137 printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
138 exit (EXIT_SUCCESS);
139 }
140
141 /* Help is requested. */
142 if (do_help)
143 usage (EXIT_SUCCESS);
144
145 /* Test argument count. */
146 if (optind + 2 != argc)
147 error (EXIT_FAILURE, 0, _("expected two arguments"));
148
149 /* Fetch the contents. */
150 fetch (argv[optind], argv[optind + 1]);
151
152 exit (EXIT_SUCCESS);
153 }
154
155 /* Display usage information and exit. */
156 static void
usage(int status)157 usage (int status)
158 {
159 if (status != EXIT_SUCCESS)
160 fprintf (stderr, _("Try `%s --help' for more information.\n"),
161 program_name);
162 else
163 {
164 printf (_("\
165 Usage: %s [OPTION] URL FILE\n\
166 "), program_name);
167 printf ("\n");
168 /* xgettext: no-wrap */
169 printf (_("\
170 Fetches and outputs the contents of an URL. If the URL cannot be accessed,\n\
171 the locally accessible FILE is used instead.\n\
172 "));
173 printf ("\n");
174 printf (_("\
175 Informative output:\n"));
176 printf (_("\
177 -h, --help display this help and exit\n"));
178 printf (_("\
179 -V, --version output version information and exit\n"));
180 printf ("\n");
181 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"),
182 stdout);
183 }
184
185 exit (status);
186 }
187
188 /* Copy a file's contents to stdout. */
189 static void
cat_file(const char * src_filename)190 cat_file (const char *src_filename)
191 {
192 int src_fd;
193 char buf[4096];
194 const int buf_size = sizeof (buf);
195
196 src_fd = open (src_filename, O_RDONLY | O_BINARY);
197 if (src_fd < 0)
198 error (EXIT_FAILURE, errno, _("error while opening \"%s\" for reading"),
199 src_filename);
200
201 for (;;)
202 {
203 ssize_t n_read = read (src_fd, buf, buf_size);
204 if (n_read < 0)
205 {
206 #ifdef EINTR
207 if (errno == EINTR)
208 continue;
209 #endif
210 error (EXIT_FAILURE, errno, _("error reading \"%s\""), src_filename);
211 }
212 if (n_read == 0)
213 break;
214
215 if (full_write (STDOUT_FILENO, buf, n_read) < n_read)
216 error (EXIT_FAILURE, errno, _("error writing stdout"));
217 }
218
219 if (close (src_fd) < 0)
220 error (EXIT_FAILURE, errno, _("error after reading \"%s\""), src_filename);
221 }
222
223 static bool
execute_it(const char * progname,const char * prog_path,char ** prog_argv,void * private_data)224 execute_it (const char *progname,
225 const char *prog_path, char **prog_argv,
226 void *private_data)
227 {
228 (void) private_data;
229
230 return execute (progname, prog_path, prog_argv, true, true, false, false,
231 true, false)
232 != 0;
233 }
234
235 /* Fetch the URL. Upon error, use the FILE as fallback. */
236 static void
fetch(const char * url,const char * file)237 fetch (const char *url, const char *file)
238 {
239 /* First try: using Java. */
240 {
241 const char *class_name = "gnu.gettext.GetURL";
242 const char *gettextjexedir;
243 const char *gettextjar;
244 const char *args[2];
245
246 #if USEJEXE
247 /* Make it possible to override the executable's location. This is
248 necessary for running the testsuite before "make install". */
249 gettextjexedir = getenv ("GETTEXTJEXEDIR");
250 if (gettextjexedir == NULL || gettextjexedir[0] == '\0')
251 gettextjexedir = relocate (GETTEXTJEXEDIR);
252 #else
253 gettextjexedir = NULL;
254 #endif
255
256 /* Make it possible to override the gettext.jar location. This is
257 necessary for running the testsuite before "make install". */
258 gettextjar = getenv ("GETTEXTJAR");
259 if (gettextjar == NULL || gettextjar[0] == '\0')
260 gettextjar = relocate (GETTEXTJAR);
261
262 /* Prepare arguments. */
263 args[0] = url;
264 args[1] = NULL;
265
266 /* Fetch the URL's contents. */
267 if (execute_java_class (class_name, &gettextjar, 1, true, gettextjexedir,
268 args,
269 false, true,
270 execute_it, NULL) == 0)
271 return;
272 }
273
274 /* Second try: using "wget -q -O - url". */
275 {
276 static bool wget_tested;
277 static bool wget_present;
278
279 if (!wget_tested)
280 {
281 /* Test for presence of wget: "wget --version > /dev/null" */
282 char *argv[3];
283 int exitstatus;
284
285 argv[0] = "wget";
286 argv[1] = "--version";
287 argv[2] = NULL;
288 exitstatus = execute ("wget", "wget", argv, false, false, true, true,
289 true, false);
290 wget_present = (exitstatus == 0);
291 wget_tested = true;
292 }
293
294 if (wget_present)
295 {
296 char *argv[8];
297 int exitstatus;
298
299 argv[0] = "wget";
300 argv[1] = "-q";
301 argv[2] = "-O"; argv[3] = "-";
302 argv[4] = "-T"; argv[5] = "30";
303 argv[6] = (char *) url;
304 argv[7] = NULL;
305 exitstatus = execute ("wget", "wget", argv, true, false, false, false,
306 true, false);
307 if (exitstatus != 127)
308 {
309 if (exitstatus != 0)
310 /* Use the file as fallback. */
311 cat_file (file);
312 return;
313 }
314 }
315 }
316
317 /* Third try: using "lynx -source url". */
318 {
319 static bool lynx_tested;
320 static bool lynx_present;
321
322 if (!lynx_tested)
323 {
324 /* Test for presence of lynx: "lynx --version > /dev/null" */
325 char *argv[3];
326 int exitstatus;
327
328 argv[0] = "lynx";
329 argv[1] = "--version";
330 argv[2] = NULL;
331 exitstatus = execute ("lynx", "lynx", argv, false, false, true, true,
332 true, false);
333 lynx_present = (exitstatus == 0);
334 lynx_tested = true;
335 }
336
337 if (lynx_present)
338 {
339 char *argv[4];
340 int exitstatus;
341
342 argv[0] = "lynx";
343 argv[1] = "-source";
344 argv[2] = (char *) url;
345 argv[3] = NULL;
346 exitstatus = execute ("lynx", "lynx", argv, true, false, false, false,
347 true, false);
348 if (exitstatus != 127)
349 {
350 if (exitstatus != 0)
351 /* Use the file as fallback. */
352 cat_file (file);
353 return;
354 }
355 }
356 }
357
358 /* Fourth try: using "curl --silent url". */
359 {
360 static bool curl_tested;
361 static bool curl_present;
362
363 if (!curl_tested)
364 {
365 /* Test for presence of curl: "curl --version > /dev/null" */
366 char *argv[3];
367 int exitstatus;
368
369 argv[0] = "curl";
370 argv[1] = "--version";
371 argv[2] = NULL;
372 exitstatus = execute ("curl", "curl", argv, false, false, true, true,
373 true, false);
374 curl_present = (exitstatus == 0 || exitstatus == 2);
375 curl_tested = true;
376 }
377
378 if (curl_present)
379 {
380 char *argv[4];
381 int exitstatus;
382
383 argv[0] = "curl";
384 argv[1] = "--silent";
385 argv[2] = (char *) url;
386 argv[3] = NULL;
387 exitstatus = execute ("curl", "curl", argv, true, false, false, false,
388 true, false);
389 if (exitstatus != 127)
390 {
391 if (exitstatus != 0)
392 /* Use the file as fallback. */
393 cat_file (file);
394 return;
395 }
396 }
397 }
398
399 /* Use the file as fallback. */
400 cat_file (file);
401 }
402