main (int argc, char **argv)
{
  char **url, **t;
  int i, ret, longindex;
  int nurl, status;
  bool append_to_log = false;

  program_name = argv[0];

  i18n_initialize ();

  /* Construct the name of the executable, without the directory part.  */
#ifdef __VMS
  /* On VMS, lose the "dev:[dir]" prefix and the ".EXE;nnn" suffix. */
  exec_name = vms_basename (argv[0]);
#else /* def __VMS */
  exec_name = strrchr (argv[0], PATH_SEPARATOR);
  if (!exec_name)
    exec_name = argv[0];
  else
    ++exec_name;
#endif /* def __VMS [else] */

#ifdef WINDOWS
  /* Drop extension (typically .EXE) from executable filename. */
  windows_main ((char **) &exec_name);
#endif

  /* Set option defaults; read the system wgetrc and ~/.wgetrc.  */
  initialize ();

  init_switches ();
  longindex = -1;
  while ((ret = getopt_long (argc, argv,
                             short_options, long_options, &longindex)) != -1)
    {
      int val;
      struct cmdline_option *opt;

      /* If LONGINDEX is unchanged, it means RET is referring a short
         option.  */
      if (longindex == -1)
        {
          if (ret == '?')
            {
              print_usage (0);
              printf ("\n");
              printf (_("Try `%s --help' for more options.\n"), exec_name);
              exit (2);
            }
          /* Find the short option character in the mapping.  */
          longindex = optmap[ret - 32];
        }
      val = long_options[longindex].val;

      /* Use the retrieved value to locate the option in the
         option_data array, and to see if we're dealing with the
         negated "--no-FOO" variant of the boolean option "--foo".  */
      opt = &option_data[val & ~BOOLEAN_NEG_MARKER];
      switch (opt->type)
        {
        case OPT_VALUE:
          setoptval (opt->data, optarg, opt->long_name);
          break;
        case OPT_BOOLEAN:
          if (optarg)
            /* The user has specified a value -- use it. */
            setoptval (opt->data, optarg, opt->long_name);
          else
            {
              /* NEG is true for `--no-FOO' style boolean options. */
              bool neg = !!(val & BOOLEAN_NEG_MARKER);
              setoptval (opt->data, neg ? "0" : "1", opt->long_name);
            }
          break;
        case OPT_FUNCALL:
          {
            void (*func) (void) = (void (*) (void)) opt->data;
            func ();
          }
          break;
        case OPT__APPEND_OUTPUT:
          setoptval ("logfile", optarg, opt->long_name);
          append_to_log = true;
          break;
        case OPT__EXECUTE:
          run_command (optarg);
          break;
        case OPT__NO:
          {
            /* We support real --no-FOO flags now, but keep these
               short options for convenience and backward
               compatibility.  */
            char *p;
            for (p = optarg; *p; p++)
              switch (*p)
                {
                case 'v':
                  setoptval ("verbose", "0", opt->long_name);
                  break;
                case 'H':
                  setoptval ("addhostdir", "0", opt->long_name);
                  break;
                case 'd':
                  setoptval ("dirstruct", "0", opt->long_name);
                  break;
                case 'c':
                  setoptval ("noclobber", "1", opt->long_name);
                  break;
                case 'p':
                  setoptval ("noparent", "1", opt->long_name);
                  break;
                default:
                  fprintf (stderr, _("%s: illegal option -- `-n%c'\n"),
                           exec_name, *p);
                  print_usage (1);
                  fprintf (stderr, "\n");
                  fprintf (stderr, _("Try `%s --help' for more options.\n"),
                           exec_name);
                  exit (1);
                }
            break;
          }
        case OPT__PARENT:
        case OPT__CLOBBER:
          {
            /* The wgetrc commands are named noparent and noclobber,
               so we must revert the meaning of the cmdline options
               before passing the value to setoptval.  */
            bool flag = true;
            if (optarg)
              flag = (*optarg == '1' || c_tolower (*optarg) == 'y'
                      || (c_tolower (optarg[0]) == 'o'
                          && c_tolower (optarg[1]) == 'n'));
            setoptval (opt->type == OPT__PARENT ? "noparent" : "noclobber",
                       flag ? "0" : "1", opt->long_name);
            break;
          }
        case OPT__DONT_REMOVE_LISTING:
          setoptval ("removelisting", "0", opt->long_name);
          break;
        }

      longindex = -1;
    }

  nurl = argc - optind;

  /* All user options have now been processed, so it's now safe to do
     interoption dependency checks. */

  if (opt.reclevel == 0)
      opt.reclevel = INFINITE_RECURSION; /* see recur.h for commentary */

  if (opt.spider || opt.delete_after)
      opt.no_dirstruct = true;

  if (opt.page_requisites && !opt.recursive)
    {
      /* Don't set opt.recursive here because it would confuse the FTP
         code.  Instead, call retrieve_tree below when either
         page_requisites or recursive is requested.  */
      opt.reclevel = 0;
      if (!opt.no_dirstruct)
        opt.dirstruct = 1;      /* normally handled by cmd_spec_recursive() */
    }

  if (opt.verbose == -1)
    opt.verbose = !opt.quiet;

  /* Sanity checks.  */
  if (opt.verbose && opt.quiet)
    {
      fprintf (stderr, _("Can't be verbose and quiet at the same time.\n"));
      print_usage (1);
      exit (1);
    }
  if (opt.timestamping && opt.noclobber)
    {
      fprintf (stderr, _("\
Can't timestamp and not clobber old files at the same time.\n"));
      print_usage (1);
      exit (1);
    }
#ifdef ENABLE_IPV6
  if (opt.ipv4_only && opt.ipv6_only)
    {
      fprintf (stderr,
               _("Cannot specify both --inet4-only and --inet6-only.\n"));
      print_usage (1);
      exit (1);
    }
#endif
  if (opt.output_document)
    {
      if (opt.convert_links
          && (nurl > 1 || opt.page_requisites || opt.recursive))
        {
          fputs (_("\
Cannot specify both -k and -O if multiple URLs are given, or in combination\n\
with -p or -r. See the manual for details.\n\n"), stderr);
          print_usage (1);
          exit (1);
        }
      if (opt.page_requisites
          || opt.recursive)
        {
          logprintf (LOG_NOTQUIET, "%s", _("\
WARNING: combining -O with -r or -p will mean that all downloaded content\n\
will be placed in the single file you specified.\n\n"));
        }
      if (opt.timestamping)
        {
          logprintf (LOG_NOTQUIET, "%s", _("\
WARNING: timestamping does nothing in combination with -O. See the manual\n\
for details.\n\n"));
          opt.timestamping = false;
        }
      if (opt.noclobber && file_exists_p(opt.output_document))
           {
              /* Check if output file exists; if it does, exit. */
              logprintf (LOG_VERBOSE,
                         _("File `%s' already there; not retrieving.\n"),
                         opt.output_document);
              exit(1);
           }
    }

  if (opt.ask_passwd && opt.passwd)
    {
      fprintf (stderr,
               _("Cannot specify both --ask-password and --password.\n"));
      print_usage (1);
      exit (1);
    }

  if (!nurl && !opt.input_filename)
    {
      /* No URL specified.  */
      fprintf (stderr, _("%s: missing URL\n"), exec_name);
      print_usage (1);
      printf ("\n");
      /* #### Something nicer should be printed here -- similar to the
         pre-1.5 `--help' page.  */
      fprintf (stderr, _("Try `%s --help' for more options.\n"), exec_name);
      exit (1);
    }

#ifdef ENABLE_IRI
  if (opt.enable_iri)
    {
      if (opt.locale && !check_encoding_name (opt.locale))
        opt.locale = NULL;

      if (!opt.locale)
        opt.locale = find_locale ();

      if (opt.encoding_remote && !check_encoding_name (opt.encoding_remote))
        opt.encoding_remote = NULL;
    }
#else
  if (opt.enable_iri || opt.locale || opt.encoding_remote)
    {
      /* sXXXav : be more specific... */
      fprintf (stderr, _("This version does not have support for IRIs\n"));
      exit(1);
    }
#endif

  if (opt.ask_passwd)
    {
      opt.passwd = prompt_for_password ();

      if (opt.passwd == NULL || opt.passwd[0] == '\0')
        exit (1);
    }

#ifdef USE_WATT32
  if (opt.wdebug)
     dbug_init();
  sock_init();
#else
  if (opt.background)
    fork_to_background ();
#endif

  /* Initialize progress.  Have to do this after the options are
     processed so we know where the log file is.  */
  if (opt.verbose)
    set_progress_implementation (opt.progress_type);

  /* Fill in the arguments.  */
  url = alloca_array (char *, nurl + 1);
  for (i = 0; i < nurl; i++, optind++)
    {
      char *rewritten = rewrite_shorthand_url (argv[optind]);
      if (rewritten)
        url[i] = rewritten;
      else
        url[i] = xstrdup (argv[optind]);
    }
  url[i] = NULL;

  /* Initialize logging.  */
  log_init (opt.lfilename, append_to_log);

  DEBUGP (("DEBUG output created by Wget %s on %s.\n\n",
           version_string, OS_TYPE));

  /* Open the output filename if necessary.  */

/* 2005-04-17 SMS.
   Note that having the output_stream ("-O") file opened here for an FTP
   URL rather than in getftp() (ftp.c) (and the http equivalent) rather
   limits the ability in VMS to open the file differently for ASCII
   versus binary FTP there.  (Of course, doing it here allows a open
   failure to be detected immediately, without first connecting to the
   server.)
*/
  if (opt.output_document)
    {
      if (HYPHENP (opt.output_document))
        {
#ifdef WINDOWS
          FILE *result;
          result = freopen ("CONOUT$", "wb", stdout);
          if (result == NULL)
            {
              logputs (LOG_NOTQUIET, _("\
WARNING: Can't reopen standard output in binary mode;\n\
         downloaded file may contain inappropriate line endings.\n"));
            }
#endif
          output_stream = stdout;
        }
      else
        {
          struct_fstat st;

#ifdef __VMS
/* Common fopen() optional arguments:
   sequential access only, access callback function.
*/
# define FOPEN_OPT_ARGS , "fop=sqo", "acc", acc_cb, &open_id
          int open_id = 7;
#else /* def __VMS */
# define FOPEN_OPT_ARGS
#endif /* def __VMS [else] */

          output_stream = fopen (opt.output_document,
                                 opt.always_rest ? "ab" : "wb"
                                 FOPEN_OPT_ARGS);
          if (output_stream == NULL)
            {
              perror (opt.output_document);
              exit (1);
            }
          if (fstat (fileno (output_stream), &st) == 0 && S_ISREG (st.st_mode))
            output_stream_regular = true;
        }
      if (!output_stream_regular && opt.convert_links)
        {
          fprintf (stderr, _("-k can be used together with -O only if \
outputting to a regular file.\n"));
          print_usage (1);
          exit(1);
        }
    }

#ifdef __VMS
  /* Set global ODS5 flag according to the specified destination (if
     any), otherwise according to the current default device.
  */
  if (output_stream == NULL)
    set_ods5_dest( "SYS$DISK");
  else if (output_stream != stdout)
    set_ods5_dest( opt.output_document);
#endif /* def __VMS */

#ifdef WINDOWS
  ws_startup ();
#endif

#ifdef SIGHUP
  /* Setup the signal handler to redirect output when hangup is
     received.  */
  if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
    signal(SIGHUP, redirect_output_signal);
#endif
  /* ...and do the same for SIGUSR1.  */
#ifdef SIGUSR1
  signal (SIGUSR1, redirect_output_signal);
#endif
#ifdef SIGPIPE
  /* Writing to a closed socket normally signals SIGPIPE, and the
     process exits.  What we want is to ignore SIGPIPE and just check
     for the return value of write().  */
  signal (SIGPIPE, SIG_IGN);
#endif
#ifdef SIGWINCH
  signal (SIGWINCH, progress_handle_sigwinch);
#endif

  status = RETROK;              /* initialize it, just-in-case */
  /* Retrieve the URLs from argument list.  */
  for (t = url; *t; t++)
    {
      char *filename = NULL, *redirected_URL = NULL;
      int dt, url_err;
      /* Need to do a new struct iri every time, because
       * retrieve_url may modify it in some circumstances,
       * currently. */
      struct iri *iri = iri_new ();
      struct url *url_parsed;

      set_uri_encoding (iri, opt.locale, true);
      url_parsed = url_parse (*t, &url_err, iri, true);

      if (!url_parsed)
        {
          char *error = url_error (*t, url_err);
          logprintf (LOG_NOTQUIET, "%s: %s.\n",*t, error);
          xfree (error);
          status = URLERROR;
        }
      else
        {
          if ((opt.recursive || opt.page_requisites)
              && (url_scheme (*t) != SCHEME_FTP || url_uses_proxy (url_parsed)))
            {
              int old_follow_ftp = opt.follow_ftp;

              /* Turn opt.follow_ftp on in case of recursive FTP retrieval */
              if (url_scheme (*t) == SCHEME_FTP)
                opt.follow_ftp = 1;

              status = retrieve_tree (url_parsed, NULL);

              opt.follow_ftp = old_follow_ftp;
            }
          else
          {
            status = retrieve_url (url_parsed, *t, &filename, &redirected_URL,
                                   NULL, &dt, opt.recursive, iri, true);
          }

          if (opt.delete_after && file_exists_p(filename))
            {
              DEBUGP (("Removing file due to --delete-after in main():\n"));
              logprintf (LOG_VERBOSE, _("Removing %s.\n"), filename);
              if (unlink (filename))
                logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
            }
          xfree_null (redirected_URL);
          xfree_null (filename);
          url_free (url_parsed);
        }
      iri_free (iri);
    }

  /* And then from the input file, if any.  */
  if (opt.input_filename)
    {
      int count;
      status = retrieve_from_file (opt.input_filename, opt.force_html, &count);
      if (!count)
        logprintf (LOG_NOTQUIET, _("No URLs found in %s.\n"),
                   opt.input_filename);
    }

  /* Print broken links. */
  if (opt.recursive && opt.spider)
    print_broken_links ();

  /* Print the downloaded sum.  */
  if ((opt.recursive || opt.page_requisites
       || nurl > 1
       || (opt.input_filename && total_downloaded_bytes != 0))
      &&
      total_downloaded_bytes != 0)
    {
      logprintf (LOG_NOTQUIET,
                 _("FINISHED --%s--\nDownloaded: %d files, %s in %s (%s)\n"),
                 datetime_str (time (NULL)),
                 numurls,
                 human_readable (total_downloaded_bytes),
                 secs_to_human_time (total_download_time),
                 retr_rate (total_downloaded_bytes, total_download_time));
      /* Print quota warning, if exceeded.  */
      if (opt.quota && total_downloaded_bytes > opt.quota)
        logprintf (LOG_NOTQUIET,
                   _("Download quota of %s EXCEEDED!\n"),
                   human_readable (opt.quota));
    }

  if (opt.cookies_output)
    save_cookies ();

  if (opt.convert_links && !opt.delete_after)
    convert_all_links ();

  log_close ();
  for (i = 0; i < nurl; i++)
    xfree (url[i]);
  cleanup ();

  return get_exit_status ();
}