xref: /netbsd-src/external/gpl2/gettext/dist/gettext-tools/src/urlget.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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