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