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. */ 66static 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. */ 75static void usage (int status) 76#if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2) 77 __attribute__ ((noreturn)) 78#endif 79; 80static void fetch (const char *url, const char *file); 81 82 83int 84main (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\ 133This is free software; see the source for copying conditions. There is NO\n\ 134warranty; 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. */ 156static void 157usage (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 (_("\ 165Usage: %s [OPTION] URL FILE\n\ 166"), program_name); 167 printf ("\n"); 168 /* xgettext: no-wrap */ 169 printf (_("\ 170Fetches and outputs the contents of an URL. If the URL cannot be accessed,\n\ 171the locally accessible FILE is used instead.\n\ 172")); 173 printf ("\n"); 174 printf (_("\ 175Informative 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. */ 189static void 190cat_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 223static bool 224execute_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. */ 236static void 237fetch (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