X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Finit.c;h=d3d0f9b3efba8c33a7e74fdcbeb00961fab58829;hp=d54c1613f5c70fce83d1c75a4fac0e7cf0c6ed29;hb=e911bc29434b7da90446d2ca5304106724d05680;hpb=29a66192cf2ceafcc434be59e052c82098a1f824 diff --git a/src/init.c b/src/init.c index d54c1613..d3d0f9b3 100644 --- a/src/init.c +++ b/src/init.c @@ -1,6 +1,5 @@ /* Reading/parsing the initialization file. - Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001 - Free Software Foundation, Inc. + Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -16,51 +15,42 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wget; if not, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +In addition, as a special exception, the Free Software Foundation +gives permission to link the code of its release of Wget with the +OpenSSL project's "OpenSSL" library (or with modified versions of it +that use the same license as the "OpenSSL" library), and distribute +the linked executables. You must obey the GNU General Public License +in all respects for all of the code used other than "OpenSSL". If you +modify this file, you may extend this exception to your version of the +file, but you are not obligated to do so. If you do not wish to do +so, delete this exception statement from your version. */ #include #include -#include #include #ifdef HAVE_UNISTD_H # include #endif -#ifdef HAVE_STRING_H -# include -#else -# include -#endif +#include #include -#ifdef WINDOWS -# include -#else -# include -# include -#ifndef __BEOS__ -# include -#endif -#endif - #ifdef HAVE_PWD_H -#include +# include #endif +#include #include "wget.h" #include "utils.h" #include "init.h" #include "host.h" -#include "recur.h" #include "netrc.h" -#include "cookies.h" /* for cookie_jar_delete */ #include "progress.h" - -#ifndef errno -extern int errno; -#endif - -extern struct cookie_jar *wget_cookie_jar; +#include "recur.h" /* for INFINITE_RECURSION */ +#include "convert.h" /* for convert_cleanup */ +#include "res.h" /* for res_cleanup */ /* We want tilde expansion enabled only when reading `.wgetrc' lines; otherwise, it will be performed by the shell. This variable will @@ -69,13 +59,15 @@ extern struct cookie_jar *wget_cookie_jar; static int enable_tilde_expansion; -#define CMD_DECLARE(func) static int func \ - PARAMS ((const char *, const char *, void *)) +#define CMD_DECLARE(func) static int func (const char *, const char *, void *) CMD_DECLARE (cmd_boolean); CMD_DECLARE (cmd_bytes); +CMD_DECLARE (cmd_bytes_large); +#ifdef HAVE_SSL +CMD_DECLARE (cmd_cert_type); +#endif CMD_DECLARE (cmd_directory_vector); -CMD_DECLARE (cmd_lockable_boolean); CMD_DECLARE (cmd_number); CMD_DECLARE (cmd_number_inf); CMD_DECLARE (cmd_string); @@ -88,19 +80,27 @@ CMD_DECLARE (cmd_spec_dirstruct); CMD_DECLARE (cmd_spec_header); CMD_DECLARE (cmd_spec_htmlify); CMD_DECLARE (cmd_spec_mirror); +CMD_DECLARE (cmd_spec_prefer_family); CMD_DECLARE (cmd_spec_progress); CMD_DECLARE (cmd_spec_recursive); +CMD_DECLARE (cmd_spec_restrict_file_names); +#ifdef HAVE_SSL +CMD_DECLARE (cmd_spec_secure_protocol); +#endif +CMD_DECLARE (cmd_spec_timeout); CMD_DECLARE (cmd_spec_useragent); -/* List of recognized commands, each consisting of name, closure and function. - When adding a new command, simply add it to the list, but be sure to keep the - list sorted alphabetically, as comind() depends on it. Also, be sure to add - any entries that allocate memory (e.g. cmd_string and cmd_vector guys) to the +/* List of recognized commands, each consisting of name, place and + function. When adding a new command, simply add it to the list, + but be sure to keep the list sorted alphabetically, as + command_by_name depends on it. Also, be sure to add any entries + that allocate memory (e.g. cmd_string and cmd_vector) to the cleanup() function below. */ + static struct { - char *name; - void *closure; - int (*action) PARAMS ((const char *, const char *, void *)); + const char *name; + void *place; + int (*action) (const char *, const char *, void *); } commands[] = { { "accept", &opt.accepts, cmd_vector }, { "addhostdir", &opt.add_hostdir, cmd_boolean }, @@ -110,49 +110,70 @@ static struct { { "backups", &opt.backups, cmd_number }, { "base", &opt.base_href, cmd_string }, { "bindaddress", &opt.bind_address, cmd_string }, +#ifdef HAVE_SSL + { "cacertificate", &opt.ca_cert, cmd_file }, +#endif { "cache", &opt.allow_cache, cmd_boolean }, +#ifdef HAVE_SSL + { "cadirectory", &opt.ca_directory, cmd_directory }, + { "certificate", &opt.cert_file, cmd_file }, + { "certificatetype", &opt.cert_type, cmd_cert_type }, + { "checkcertificate", &opt.check_cert, cmd_boolean }, +#endif + { "connecttimeout", &opt.connect_timeout, cmd_time }, { "continue", &opt.always_rest, cmd_boolean }, { "convertlinks", &opt.convert_links, cmd_boolean }, { "cookies", &opt.cookies, cmd_boolean }, { "cutdirs", &opt.cut_dirs, cmd_number }, -#ifdef DEBUG +#ifdef ENABLE_DEBUG { "debug", &opt.debug, cmd_boolean }, #endif { "deleteafter", &opt.delete_after, cmd_boolean }, { "dirprefix", &opt.dir_prefix, cmd_directory }, { "dirstruct", NULL, cmd_spec_dirstruct }, + { "dnscache", &opt.dns_cache, cmd_boolean }, + { "dnstimeout", &opt.dns_timeout, cmd_time }, { "domains", &opt.domains, cmd_vector }, { "dotbytes", &opt.dot_bytes, cmd_bytes }, { "dotsinline", &opt.dots_in_line, cmd_number }, { "dotspacing", &opt.dot_spacing, cmd_number }, { "dotstyle", &opt.dot_style, cmd_string }, #ifdef HAVE_SSL - { "egdfile", &opt.sslegdsock, cmd_file }, + { "egdfile", &opt.egd_file, cmd_file }, #endif { "excludedirectories", &opt.excludes, cmd_directory_vector }, { "excludedomains", &opt.exclude_domains, cmd_vector }, { "followftp", &opt.follow_ftp, cmd_boolean }, { "followtags", &opt.follow_tags, cmd_vector }, { "forcehtml", &opt.force_html, cmd_boolean }, + { "ftppasswd", &opt.ftp_passwd, cmd_string }, /* deprecated */ + { "ftppassword", &opt.ftp_passwd, cmd_string }, { "ftpproxy", &opt.ftp_proxy, cmd_string }, + { "ftpuser", &opt.ftp_user, cmd_string }, { "glob", &opt.ftp_glob, cmd_boolean }, { "header", NULL, cmd_spec_header }, { "htmlextension", &opt.html_extension, cmd_boolean }, { "htmlify", NULL, cmd_spec_htmlify }, { "httpkeepalive", &opt.http_keep_alive, cmd_boolean }, - { "httppasswd", &opt.http_passwd, cmd_string }, + { "httppasswd", &opt.http_passwd, cmd_string }, /* deprecated */ + { "httppassword", &opt.http_passwd, cmd_string }, { "httpproxy", &opt.http_proxy, cmd_string }, { "httpsproxy", &opt.https_proxy, cmd_string }, { "httpuser", &opt.http_user, cmd_string }, { "ignorelength", &opt.ignore_length, cmd_boolean }, { "ignoretags", &opt.ignore_tags, cmd_vector }, { "includedirectories", &opt.includes, cmd_directory_vector }, +#ifdef ENABLE_IPV6 + { "inet4only", &opt.ipv4_only, cmd_boolean }, + { "inet6only", &opt.ipv6_only, cmd_boolean }, +#endif { "input", &opt.input_filename, cmd_file }, + { "keepsessioncookies", &opt.keep_session_cookies, cmd_boolean }, { "killlonger", &opt.kill_longer, cmd_boolean }, { "limitrate", &opt.limit_rate, cmd_bytes }, { "loadcookies", &opt.cookies_input, cmd_file }, { "logfile", &opt.lfilename, cmd_file }, - { "login", &opt.ftp_acc, cmd_string }, + { "login", &opt.ftp_user, cmd_string },/* deprecated*/ { "mirror", NULL, cmd_spec_mirror }, { "netrc", &opt.netrc, cmd_boolean }, { "noclobber", &opt.noclobber, cmd_boolean }, @@ -161,55 +182,73 @@ static struct { { "numtries", &opt.ntry, cmd_number_inf },/* deprecated*/ { "outputdocument", &opt.output_document, cmd_file }, { "pagerequisites", &opt.page_requisites, cmd_boolean }, - { "passiveftp", &opt.ftp_pasv, cmd_lockable_boolean }, - { "passwd", &opt.ftp_pass, cmd_string }, + { "passiveftp", &opt.ftp_pasv, cmd_boolean }, + { "passwd", &opt.ftp_passwd, cmd_string },/* deprecated*/ + { "password", &opt.passwd, cmd_string }, { "postdata", &opt.post_data, cmd_string }, { "postfile", &opt.post_file_name, cmd_file }, + { "preferfamily", NULL, cmd_spec_prefer_family }, + { "preservepermissions", &opt.preserve_perm, cmd_boolean }, +#ifdef HAVE_SSL + { "privatekey", &opt.private_key, cmd_file }, + { "privatekeytype", &opt.private_key_type, cmd_cert_type }, +#endif { "progress", &opt.progress_type, cmd_spec_progress }, - { "proxypasswd", &opt.proxy_passwd, cmd_string }, + { "protocoldirectories", &opt.protocol_directories, cmd_boolean }, + { "proxypasswd", &opt.proxy_passwd, cmd_string }, /* deprecated */ + { "proxypassword", &opt.proxy_passwd, cmd_string }, { "proxyuser", &opt.proxy_user, cmd_string }, { "quiet", &opt.quiet, cmd_boolean }, - { "quota", &opt.quota, cmd_bytes }, + { "quota", &opt.quota, cmd_bytes_large }, +#ifdef HAVE_SSL + { "randomfile", &opt.random_file, cmd_file }, +#endif { "randomwait", &opt.random_wait, cmd_boolean }, + { "readtimeout", &opt.read_timeout, cmd_time }, { "reclevel", &opt.reclevel, cmd_number_inf }, { "recursive", NULL, cmd_spec_recursive }, { "referer", &opt.referer, cmd_string }, { "reject", &opt.rejects, cmd_vector }, { "relativeonly", &opt.relative_only, cmd_boolean }, { "removelisting", &opt.remove_listing, cmd_boolean }, + { "restrictfilenames", NULL, cmd_spec_restrict_file_names }, { "retrsymlinks", &opt.retr_symlinks, cmd_boolean }, + { "retryconnrefused", &opt.retry_connrefused, cmd_boolean }, { "robots", &opt.use_robots, cmd_boolean }, { "savecookies", &opt.cookies_output, cmd_file }, { "saveheaders", &opt.save_headers, cmd_boolean }, +#ifdef HAVE_SSL + { "secureprotocol", &opt.secure_protocol, cmd_spec_secure_protocol }, +#endif { "serverresponse", &opt.server_response, cmd_boolean }, { "spanhosts", &opt.spanhost, cmd_boolean }, { "spider", &opt.spider, cmd_boolean }, -#ifdef HAVE_SSL - { "sslcertfile", &opt.sslcertfile, cmd_file }, - { "sslcertkey", &opt.sslcertkey, cmd_file }, -#endif /* HAVE_SSL */ - { "timeout", &opt.timeout, cmd_time }, + { "strictcomments", &opt.strict_comments, cmd_boolean }, + { "timeout", NULL, cmd_spec_timeout }, { "timestamping", &opt.timestamping, cmd_boolean }, { "tries", &opt.ntry, cmd_number_inf }, { "useproxy", &opt.use_proxy, cmd_boolean }, + { "user", &opt.user, cmd_string }, { "useragent", NULL, cmd_spec_useragent }, { "verbose", &opt.verbose, cmd_boolean }, { "wait", &opt.wait, cmd_time }, { "waitretry", &opt.waitretry, cmd_time } }; -/* Look up COM in the commands[] array and return its index. If COM - is not found, -1 is returned. This function uses binary search. */ +/* Look up CMDNAME in the commands[] and return its position in the + array. If CMDNAME is not found, return -1. */ static int -comind (const char *com) +command_by_name (const char *cmdname) { - int lo = 0, hi = ARRAY_SIZE (commands) - 1; + /* Use binary search for speed. Wget has ~100 commands, which + guarantees a worst case performance of 7 string comparisons. */ + int lo = 0, hi = countof (commands) - 1; while (lo <= hi) { int mid = (lo + hi) >> 1; - int cmp = strcasecmp (com, commands[mid].name); + int cmp = strcasecmp (cmdname, commands[mid].name); if (cmp < 0) hi = mid - 1; else if (cmp > 0) @@ -231,17 +270,13 @@ defaults (void) NULL this way is technically illegal, but porting Wget to a machine where NULL is not all-zero bit pattern will be the least of the implementors' worries. */ - memset (&opt, 0, sizeof (opt)); + xzero (opt); opt.cookies = 1; - opt.verbose = -1; - opt.dir_prefix = xstrdup ("."); opt.ntry = 20; opt.reclevel = 5; opt.add_hostdir = 1; - opt.ftp_acc = xstrdup ("anonymous"); - opt.ftp_pass = xstrdup ("-wget@"); opt.netrc = 1; opt.ftp_glob = 1; opt.htmlify = 1; @@ -252,9 +287,7 @@ defaults (void) opt.no_proxy = sepstring (tmp); opt.allow_cache = 1; -#ifdef HAVE_SELECT - opt.timeout = 900; -#endif + opt.read_timeout = 900; opt.use_robots = 1; opt.remove_listing = 1; @@ -262,6 +295,21 @@ defaults (void) opt.dot_bytes = 1024; opt.dot_spacing = 10; opt.dots_in_line = 50; + + opt.dns_cache = 1; + opt.ftp_pasv = 1; + +#ifdef HAVE_SSL + opt.check_cert = 1; +#endif + + /* The default for file name restriction defaults to the OS type. */ +#if !defined(WINDOWS) && !defined(__CYGWIN__) + opt.restrict_files_os = restrict_unix; +#else + opt.restrict_files_os = restrict_windows; +#endif + opt.restrict_files_ctrl = 1; } /* Return the user's home directory (strdup-ed), or NULL if none is @@ -281,9 +329,9 @@ home_dir (void) return NULL; home = pwd->pw_dir; #else /* WINDOWS */ - home = "C:\\"; - /* #### Maybe I should grab home_dir from registry, but the best - that I could get from there is user's Start menu. It sucks! */ + /* Under Windows, if $HOME isn't defined, use the directory where + `wget.exe' resides. */ + home = ws_mypath (); #endif /* WINDOWS */ } @@ -307,33 +355,31 @@ wgetrc_file_name (void) { if (!file_exists_p (env)) { - fprintf (stderr, "%s: %s: %s.\n", exec_name, env, strerror (errno)); + fprintf (stderr, _("%s: WGETRC points to %s, which doesn't exist.\n"), + exec_name, env); exit (1); } return xstrdup (env); } -#ifndef WINDOWS /* If that failed, try $HOME/.wgetrc. */ home = home_dir (); if (home) - { - file = (char *)xmalloc (strlen (home) + 1 + strlen (".wgetrc") + 1); - sprintf (file, "%s/.wgetrc", home); - } - FREE_MAYBE (home); -#else /* WINDOWS */ - /* Under Windows, "home" is (for the purposes of this function) the - directory where `wget.exe' resides, and `wget.ini' will be used - as file name. SYSTEM_WGETRC should not be defined under WINDOWS. + file = aprintf ("%s/.wgetrc", home); + xfree_null (home); - It is not as trivial as I assumed, because on 95 argv[0] is full - path, but on NT you get what you typed in command line. --dbudor */ - home = ws_mypath (); - if (home) +#ifdef WINDOWS + /* Under Windows, if we still haven't found .wgetrc, look for the file + `wget.ini' in the directory where `wget.exe' resides; we do this for + backward compatibility with previous versions of Wget. + SYSTEM_WGETRC should not be defined under WINDOWS. */ + if (!file || !file_exists_p (file)) { - file = (char *)xmalloc (strlen (home) + strlen ("wget.ini") + 1); - sprintf (file, "%swget.ini", home); + xfree_null (file); + file = NULL; + home = ws_mypath (); + if (home) + file = aprintf ("%s/wget.ini", home); } #endif /* WINDOWS */ @@ -347,47 +393,78 @@ wgetrc_file_name (void) return file; } -/* Initialize variables from a wgetrc file */ -static void +/* Return values of parse_line. */ +enum parse_line { + line_ok, + line_empty, + line_syntax_error, + line_unknown_command +}; + +static enum parse_line parse_line (const char *, char **, char **, int *); +static int setval_internal (int, const char *, const char *); + +/* Initialize variables from a wgetrc file. Returns zero (failure) if + there were errors in the file. */ + +static int run_wgetrc (const char *file) { FILE *fp; char *line; int ln; + int errcnt = 0; fp = fopen (file, "rb"); if (!fp) { fprintf (stderr, _("%s: Cannot read %s (%s).\n"), exec_name, file, strerror (errno)); - return; + return 1; /* not a fatal error */ } enable_tilde_expansion = 1; ln = 1; - while ((line = read_whole_line (fp))) + while ((line = read_whole_line (fp)) != NULL) { - char *com, *val; - int status; + char *com = NULL, *val = NULL; + int comind; /* Parse the line. */ - status = parse_line (line, &com, &val); - xfree (line); - /* If everything is OK, set the value. */ - if (status == 1) + switch (parse_line (line, &com, &val, &comind)) { - if (!setval (com, val)) - fprintf (stderr, _("%s: Error in %s at line %d.\n"), exec_name, - file, ln); - xfree (com); - xfree (val); + case line_ok: + /* If everything is OK, set the value. */ + if (!setval_internal (comind, com, val)) + { + fprintf (stderr, _("%s: Error in %s at line %d.\n"), + exec_name, file, ln); + ++errcnt; + } + break; + case line_syntax_error: + fprintf (stderr, _("%s: Syntax error in %s at line %d.\n"), + exec_name, file, ln); + ++errcnt; + break; + case line_unknown_command: + fprintf (stderr, _("%s: Unknown command `%s' in %s at line %d.\n"), + exec_name, com, file, ln); + ++errcnt; + break; + case line_empty: + break; + default: + abort (); } - else if (status == 0) - fprintf (stderr, _("%s: Error in %s at line %d.\n"), exec_name, - file, ln); + xfree_null (com); + xfree_null (val); + xfree (line); ++ln; } enable_tilde_expansion = 0; fclose (fp); + + return errcnt == 0; } /* Initialize the defaults and run the system wgetrc and user's own @@ -396,6 +473,7 @@ void initialize (void) { char *file; + int ok = 1; /* Load the hard-coded defaults. */ defaults (); @@ -403,14 +481,14 @@ initialize (void) /* If SYSTEM_WGETRC is defined, use it. */ #ifdef SYSTEM_WGETRC if (file_exists_p (SYSTEM_WGETRC)) - run_wgetrc (SYSTEM_WGETRC); + ok &= run_wgetrc (SYSTEM_WGETRC); #endif /* Override it with your own, if one exists. */ file = wgetrc_file_name (); if (!file) return; - /* #### We should somehow canonicalize `file' and SYSTEM_WGETRC, - really. */ + /* #### We should canonicalize `file' and SYSTEM_WGETRC with + something like realpath() before comparing them with `strcmp' */ #ifdef SYSTEM_WGETRC if (!strcmp (file, SYSTEM_WGETRC)) { @@ -420,267 +498,288 @@ initialize (void) } else #endif - run_wgetrc (file); + ok &= run_wgetrc (file); + + /* If there were errors processing either `.wgetrc', abort. */ + if (!ok) + exit (2); + xfree (file); return; } +/* Remove dashes and underscores from S, modifying S in the + process. */ + +static void +dehyphen (char *s) +{ + char *t = s; /* t - tortoise */ + char *h = s; /* h - hare */ + while (*h) + if (*h == '_' || *h == '-') + ++h; + else + *t++ = *h++; + *t = '\0'; +} + /* Parse the line pointed by line, with the syntax: - * command * = * value + * command * = * value * Uses malloc to allocate space for command and value. If the line is invalid, data is freed and 0 is returned. - Return values: - 1 - success - 0 - failure - -1 - empty */ -int -parse_line (const char *line, char **com, char **val) + Returns one of line_ok, line_empty, line_syntax_error, or + line_unknown_command. + + In case of line_ok, *COM and *VAL point to freshly allocated + strings, and *COMIND points to com's index. In case of error or + empty line, their values are unmodified. */ + +static enum parse_line +parse_line (const char *line, char **com, char **val, int *comind) { - const char *p = line; - const char *orig_comptr, *end; - char *new_comptr; + const char *p; + const char *end = line + strlen (line); + const char *cmdstart, *cmdend; + const char *valstart, *valend; + + char *cmdcopy; + int ind; + + /* Skip leading and trailing whitespace. */ + while (*line && ISSPACE (*line)) + ++line; + while (end > line && ISSPACE (end[-1])) + --end; + + /* Skip empty lines and comments. */ + if (!*line || *line == '#') + return line_empty; + + p = line; + + cmdstart = p; + while (p < end && (ISALPHA (*p) || *p == '_' || *p == '-')) + ++p; + cmdend = p; - /* Skip whitespace. */ - while (*p && ISSPACE (*p)) + /* Skip '=', as well as any space before or after it. */ + while (p < end && ISSPACE (*p)) + ++p; + if (p == end || *p != '=') + return line_syntax_error; + ++p; + while (p < end && ISSPACE (*p)) ++p; - /* Don't process empty lines. */ - if (!*p || *p == '#') - return -1; + valstart = p; + valend = end; - for (orig_comptr = p; ISALPHA (*p) || *p == '_' || *p == '-'; p++) - ; - /* The next char should be space or '='. */ - if (!ISSPACE (*p) && (*p != '=')) - return 0; - /* Here we cannot use strdupdelim() as we normally would because we - want to skip the `-' and `_' characters in the input string. */ - *com = (char *)xmalloc (p - orig_comptr + 1); - for (new_comptr = *com; orig_comptr < p; orig_comptr++) - { - if (*orig_comptr == '_' || *orig_comptr == '-') - continue; - *new_comptr++ = *orig_comptr; - } - *new_comptr = '\0'; - /* If the command is invalid, exit now. */ - if (comind (*com) == -1) - { - xfree (*com); - return 0; - } + /* The syntax is valid (even though the command might not be). Fill + in the command name and value. */ + *com = strdupdelim (cmdstart, cmdend); + *val = strdupdelim (valstart, valend); - /* Skip spaces before '='. */ - for (; ISSPACE (*p); p++); - /* If '=' not found, bail out. */ - if (*p != '=') - { - xfree (*com); - return 0; - } - /* Skip spaces after '='. */ - for (++p; ISSPACE (*p); p++); - /* Get the ending position for VAL by starting with the end of the - line and skipping whitespace. */ - end = line + strlen (line) - 1; - while (end > p && ISSPACE (*end)) - --end; - *val = strdupdelim (p, end + 1); - return 1; + /* The line now known to be syntactically correct. Check whether + the command is valid. */ + BOUNDED_TO_ALLOCA (cmdstart, cmdend, cmdcopy); + dehyphen (cmdcopy); + ind = command_by_name (cmdcopy); + if (ind == -1) + return line_unknown_command; + + /* Report success to the caller. */ + *comind = ind; + return line_ok; } -/* Set COM to VAL. This is the meat behind processing `.wgetrc'. No - fatals -- error signal prints a warning and resets to default - value. All error messages are printed to stderr, *not* to - opt.lfile, since opt.lfile wasn't even generated yet. */ -int -setval (const char *com, const char *val) +/* Run commands[comind].action. */ + +static int +setval_internal (int comind, const char *com, const char *val) { - int ind; + assert (0 <= comind && comind < countof (commands)); + DEBUGP (("Setting %s (%s) to %s\n", com, commands[comind].name, val)); + return commands[comind].action (com, val, commands[comind].place); +} - if (!com || !val) - return 0; - ind = comind (com); - if (ind == -1) +/* Run command COM with value VAL. If running the command produces an + error, report the error and exit. + + This is intended to be called from main() to modify Wget's behavior + through command-line switches. Since COM is hard-coded in main(), + it is not canonicalized, and this aborts when COM is not found. + + If COMIND's are exported to init.h, this function will be changed + to accept COMIND directly. */ + +void +setoptval (const char *com, const char *val, const char *optname) +{ + /* Prepend "--" to OPTNAME. */ + char *dd_optname = (char *) alloca (2 + strlen (optname) + 1); + dd_optname[0] = '-'; + dd_optname[1] = '-'; + strcpy (dd_optname + 2, optname); + + assert (val != NULL); + if (!setval_internal (command_by_name (com), dd_optname, val)) + exit (2); +} + +/* Parse OPT into command and value and run it. For example, + run_command("foo=bar") is equivalent to setoptval("foo", "bar"). + This is used by the `--execute' flag in main.c. */ + +void +run_command (const char *opt) +{ + char *com, *val; + int comind; + switch (parse_line (opt, &com, &val, &comind)) { - /* #### Should I just abort()? */ -#ifdef DEBUG - fprintf (stderr, _("%s: BUG: unknown command `%s', value `%s'.\n"), - exec_name, com, val); -#endif - return 0; + case line_ok: + if (!setval_internal (comind, com, val)) + exit (2); + xfree (com); + xfree (val); + break; + default: + fprintf (stderr, _("%s: Invalid --execute command `%s'\n"), + exec_name, opt); + exit (2); } - return ((*commands[ind].action) (com, val, commands[ind].closure)); } /* Generic helper functions, for use with `commands'. */ -static int myatoi PARAMS ((const char *s)); +/* Forward declarations: */ +struct decode_item { + const char *name; + int code; +}; +static int decode_string (const char *, const struct decode_item *, int, int *); +static int simple_atoi (const char *, const char *, int *); +static int simple_atof (const char *, const char *, double *); -/* Store the boolean value from VAL to CLOSURE. COM is ignored, +#define CMP1(p, c0) (TOLOWER((p)[0]) == (c0) && (p)[1] == '\0') + +#define CMP2(p, c0, c1) (TOLOWER((p)[0]) == (c0) \ + && TOLOWER((p)[1]) == (c1) \ + && (p)[2] == '\0') + +#define CMP3(p, c0, c1, c2) (TOLOWER((p)[0]) == (c0) \ + && TOLOWER((p)[1]) == (c1) \ + && TOLOWER((p)[2]) == (c2) \ + && (p)[3] == '\0') + + +/* Store the boolean value from VAL to PLACE. COM is ignored, except for error messages. */ static int -cmd_boolean (const char *com, const char *val, void *closure) +cmd_boolean (const char *com, const char *val, void *place) { int bool_value; - if (!strcasecmp (val, "on") - || (*val == '1' && !*(val + 1))) + if (CMP2 (val, 'o', 'n') || CMP3 (val, 'y', 'e', 's') || CMP1 (val, '1')) + /* "on", "yes" and "1" mean true. */ bool_value = 1; - else if (!strcasecmp (val, "off") - || (*val == '0' && !*(val + 1))) + else if (CMP3 (val, 'o', 'f', 'f') || CMP2 (val, 'n', 'o') || CMP1 (val, '0')) + /* "off", "no" and "0" mean false. */ bool_value = 0; else { - fprintf (stderr, _("%s: %s: Please specify on or off.\n"), - exec_name, com); - return 0; - } - - *(int *)closure = bool_value; - return 1; -} - -/* Store the lockable_boolean {2, 1, 0, -1} value from VAL to CLOSURE. COM is - ignored, except for error messages. Values 2 and -1 indicate that once - defined, the value may not be changed by successive wgetrc files or - command-line arguments. - - Values: 2 - Enable a particular option for good ("always") - 1 - Enable an option ("on") - 0 - Disable an option ("off") - -1 - Disable an option for good ("never") */ -static int -cmd_lockable_boolean (const char *com, const char *val, void *closure) -{ - int lockable_boolean_value; - - /* - * If a config file said "always" or "never", don't allow command line - * arguments to override the config file. - */ - if (*(int *)closure == -1 || *(int *)closure == 2) - return 1; - - if (!strcasecmp (val, "always") - || (*val == '2' && !*(val + 1))) - lockable_boolean_value = 2; - else if (!strcasecmp (val, "on") - || (*val == '1' && !*(val + 1))) - lockable_boolean_value = 1; - else if (!strcasecmp (val, "off") - || (*val == '0' && !*(val + 1))) - lockable_boolean_value = 0; - else if (!strcasecmp (val, "never") - || (*val == '-' && *(val + 1) == '1' && !*(val + 2))) - lockable_boolean_value = -1; - else - { - fprintf (stderr, _("%s: %s: Please specify always, on, off, " - "or never.\n"), - exec_name, com); + fprintf (stderr, + _("%s: %s: Invalid boolean `%s'; use `on' or `off'.\n"), + exec_name, com, val); return 0; } - *(int *)closure = lockable_boolean_value; + *(int *)place = bool_value; return 1; } -/* Set the non-negative integer value from VAL to CLOSURE. With +/* Set the non-negative integer value from VAL to PLACE. With incorrect specification, the number remains unchanged. */ static int -cmd_number (const char *com, const char *val, void *closure) +cmd_number (const char *com, const char *val, void *place) { - int num = myatoi (val); - - if (num == -1) + if (!simple_atoi (val, val + strlen (val), place) + || *(int *) place < 0) { - fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"), + fprintf (stderr, _("%s: %s: Invalid number `%s'.\n"), exec_name, com, val); return 0; } - *(int *)closure = num; return 1; } /* Similar to cmd_number(), only accepts `inf' as a synonym for 0. */ static int -cmd_number_inf (const char *com, const char *val, void *closure) +cmd_number_inf (const char *com, const char *val, void *place) { if (!strcasecmp (val, "inf")) { - *(int *)closure = 0; + *(int *)place = 0; return 1; } - return cmd_number (com, val, closure); + return cmd_number (com, val, place); } /* Copy (strdup) the string at COM to a new location and place a - pointer to *CLOSURE. */ + pointer to *PLACE. */ static int -cmd_string (const char *com, const char *val, void *closure) +cmd_string (const char *com, const char *val, void *place) { - char **pstring = (char **)closure; + char **pstring = (char **)place; - FREE_MAYBE (*pstring); + xfree_null (*pstring); *pstring = xstrdup (val); return 1; } +#ifndef WINDOWS +# define ISSEP(c) ((c) == '/') +#else +# define ISSEP(c) ((c) == '/' || (c) == '\\') +#endif + /* Like the above, but handles tilde-expansion when reading a user's `.wgetrc'. In that case, and if VAL begins with `~', the tilde gets expanded to the user's home directory. */ static int -cmd_file (const char *com, const char *val, void *closure) +cmd_file (const char *com, const char *val, void *place) { - char **pstring = (char **)closure; + char **pstring = (char **)place; - FREE_MAYBE (*pstring); + xfree_null (*pstring); - /* #### If VAL is empty, perhaps should set *CLOSURE to NULL. */ + /* #### If VAL is empty, perhaps should set *PLACE to NULL. */ - if (!enable_tilde_expansion || !(*val == '~' && (*(val + 1) == '/' -#ifdef WINDOWS - || *(val + 1) == '\\' -#endif - ))) + if (!enable_tilde_expansion || !(*val == '~' && ISSEP (val[1]))) { noexpand: *pstring = xstrdup (val); } else { - char *result; int homelen; char *home = home_dir (); if (!home) goto noexpand; homelen = strlen (home); - while (homelen && (home[homelen - 1] == '/' -#ifdef WINDOWS - || home[homelen - 1] == '\\' -#endif - )) + while (homelen && ISSEP (home[homelen - 1])) home[--homelen] = '\0'; /* Skip the leading "~/". */ -#ifdef WINDOWS - for (++val; *val == '/' || *val == '\\'; val++) + for (++val; ISSEP (*val); val++) ; -#else - for (++val; *val == '/'; val++) - ; -#endif - result = xmalloc (homelen + 1 + strlen (val)); - memcpy (result, home, homelen); - result[homelen] = '/'; - strcpy (result + homelen + 1, val); - - *pstring = result; + *pstring = concat_strings (home, "/", val, (char *) 0); } + #ifdef WINDOWS /* Convert "\" to "/". */ { @@ -695,17 +794,17 @@ cmd_file (const char *com, const char *val, void *closure) /* Like cmd_file, but strips trailing '/' characters. */ static int -cmd_directory (const char *com, const char *val, void *closure) +cmd_directory (const char *com, const char *val, void *place) { char *s, *t; /* Call cmd_file() for tilde expansion and separator canonicalization (backslash -> slash under Windows). These things should perhaps be in a separate function. */ - if (!cmd_file (com, val, closure)) + if (!cmd_file (com, val, place)) return 0; - s = *(char **)closure; + s = *(char **)place; t = s + strlen (s); while (t > s && *--t == '/') *t = '\0'; @@ -713,13 +812,14 @@ cmd_directory (const char *com, const char *val, void *closure) return 1; } -/* Merge the vector (array of strings separated with `,') in COM with - the vector (NULL-terminated array of strings) pointed to by - CLOSURE. */ +/* Split VAL by space to a vector of values, and append those values + to vector pointed to by the PLACE argument. If VAL is empty, the + PLACE vector is cleared instead. */ + static int -cmd_vector (const char *com, const char *val, void *closure) +cmd_vector (const char *com, const char *val, void *place) { - char ***pvec = (char ***)closure; + char ***pvec = (char ***)place; if (*val) *pvec = merge_vecs (*pvec, sepstring (val)); @@ -732,9 +832,9 @@ cmd_vector (const char *com, const char *val, void *closure) } static int -cmd_directory_vector (const char *com, const char *val, void *closure) +cmd_directory_vector (const char *com, const char *val, void *place) { - char ***pvec = (char ***)closure; + char ***pvec = (char ***)place; if (*val) { @@ -762,122 +862,193 @@ cmd_directory_vector (const char *com, const char *val, void *closure) return 1; } -/* Set the value stored in VAL to CLOSURE (which should point to a - long int), allowing several postfixes, with the following syntax - (regexp): - - [0-9]+ -> bytes - [0-9]+[kK] -> bytes * 1024 - [0-9]+[mM] -> bytes * 1024 * 1024 - inf -> 0 +/* Engine for cmd_bytes and cmd_bytes_large: converts a string such as + "100k" or "2.5G" to a floating point number. */ - Anything else is flagged as incorrect, and CLOSURE is unchanged. */ static int -cmd_bytes (const char *com, const char *val, void *closure) +parse_bytes_helper (const char *val, double *result) { - long result; - long *out = (long *)closure; - const char *p; + double number, mult; + const char *end = val + strlen (val); - result = 0; - p = val; /* Check for "inf". */ - if (p[0] == 'i' && p[1] == 'n' && p[2] == 'f' && p[3] == '\0') + if (0 == strcmp (val, "inf")) { - *out = 0; + *result = 0; return 1; } - /* Search for digits and construct result. */ - for (; *p && ISDIGIT (*p); p++) - result = (10 * result) + (*p - '0'); - /* If no digits were found, or more than one character is following - them, bail out. */ - if (p == val || (*p != '\0' && *(p + 1) != '\0')) - { - printf (_("%s: Invalid specification `%s'\n"), com, val); - return 0; - } - /* Search for a designator. */ - switch (TOLOWER (*p)) + + /* Strip trailing whitespace. */ + while (val < end && ISSPACE (end[-1])) + --end; + if (val == end) + return 0; + + switch (TOLOWER (end[-1])) { - case '\0': - /* None */ - break; case 'k': - /* Kilobytes */ - result *= 1024; + --end, mult = 1024.0; break; case 'm': - /* Megabytes */ - result *= (long)1024 * 1024; + --end, mult = 1048576.0; break; case 'g': - /* Gigabytes */ - result *= (long)1024 * 1024 * 1024; + --end, mult = 1073741824.0; + break; + case 't': + --end, mult = 1099511627776.0; break; default: - printf (_("%s: Invalid specification `%s'\n"), com, val); + /* Not a recognized suffix: assume it's a digit. (If not, + simple_atof will raise an error.) */ + mult = 1; + } + + /* Skip leading and trailing whitespace. */ + while (val < end && ISSPACE (*val)) + ++val; + while (val < end && ISSPACE (end[-1])) + --end; + if (val == end) + return 0; + + if (!simple_atof (val, end, &number) || number < 0) + return 0; + + *result = number * mult; + return 1; +} + +/* Parse VAL as a number and set its value to PLACE (which should + point to a wgint). + + By default, the value is assumed to be in bytes. If "K", "M", or + "G" are appended, the value is multiplied with 1<<10, 1<<20, or + 1<<30, respectively. Floating point values are allowed and are + cast to integer before use. The idea is to be able to use things + like 1.5k instead of "1536". + + The string "inf" is returned as 0. + + In case of error, 0 is returned and memory pointed to by PLACE + remains unmodified. */ + +static int +cmd_bytes (const char *com, const char *val, void *place) +{ + double byte_value; + if (!parse_bytes_helper (val, &byte_value)) + { + fprintf (stderr, _("%s: %s: Invalid byte value `%s'\n"), + exec_name, com, val); return 0; } - *out = result; + *(wgint *)place = (wgint)byte_value; return 1; } -/* Store the value of VAL to *OUT, allowing suffixes for minutes and - hours. */ +/* Like cmd_bytes, but PLACE is interpreted as a pointer to + LARGE_INT. It works by converting the string to double, therefore + working with values up to 2^53-1 without loss of precision. This + value (8192 TB) is large enough to serve for a while. */ + static int -cmd_time (const char *com, const char *val, void *closure) +cmd_bytes_large (const char *com, const char *val, void *place) { - long result = 0; - const char *p = val; - - /* Search for digits and construct result. */ - for (; *p && ISDIGIT (*p); p++) - result = (10 * result) + (*p - '0'); - /* If no digits were found, or more than one character is following - them, bail out. */ - if (p == val || (*p != '\0' && *(p + 1) != '\0')) + double byte_value; + if (!parse_bytes_helper (val, &byte_value)) { - printf (_("%s: Invalid specification `%s'\n"), com, val); + fprintf (stderr, _("%s: %s: Invalid byte value `%s'\n"), + exec_name, com, val); return 0; } - /* Search for a suffix. */ - switch (TOLOWER (*p)) + *(LARGE_INT *)place = (LARGE_INT)byte_value; + return 1; +} + +/* Store the value of VAL to *OUT. The value is a time period, by + default expressed in seconds, but also accepting suffixes "m", "h", + "d", and "w" for minutes, hours, days, and weeks respectively. */ + +static int +cmd_time (const char *com, const char *val, void *place) +{ + double number, mult; + const char *end = val + strlen (val); + + /* Strip trailing whitespace. */ + while (val < end && ISSPACE (end[-1])) + --end; + + if (val == end) { - case '\0': - /* None */ + err: + fprintf (stderr, _("%s: %s: Invalid time period `%s'\n"), + exec_name, com, val); + return 0; + } + + switch (TOLOWER (end[-1])) + { + case 's': + --end, mult = 1; /* seconds */ break; case 'm': - /* Minutes */ - result *= 60; + --end, mult = 60; /* minutes */ break; case 'h': - /* Seconds */ - result *= 3600; + --end, mult = 3600; /* hours */ break; case 'd': - /* Days (overflow on 16bit machines) */ - result *= 86400L; + --end, mult = 86400.0; /* days */ break; case 'w': - /* Weeks :-) */ - result *= 604800L; + --end, mult = 604800.0; /* weeks */ break; default: - printf (_("%s: Invalid specification `%s'\n"), com, val); - return 0; + /* Not a recognized suffix: assume it belongs to the number. + (If not, simple_atof will raise an error.) */ + mult = 1; } - *(long *)closure = result; + + /* Skip leading and trailing whitespace. */ + while (val < end && ISSPACE (*val)) + ++val; + while (val < end && ISSPACE (end[-1])) + --end; + if (val == end) + goto err; + + if (!simple_atof (val, end, &number)) + goto err; + + *(double *)place = number * mult; return 1; } + +#ifdef HAVE_SSL +static int +cmd_cert_type (const char *com, const char *val, void *place) +{ + static const struct decode_item choices[] = { + { "pem", keyfile_pem }, + { "der", keyfile_asn1 }, + { "asn1", keyfile_asn1 }, + }; + int ok = decode_string (val, choices, countof (choices), place); + if (!ok) + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); + return ok; +} +#endif /* Specialized helper functions, used by `commands' to handle some options specially. */ -static int check_user_specified_header PARAMS ((const char *)); +static int check_user_specified_header (const char *); static int -cmd_spec_dirstruct (const char *com, const char *val, void *closure) +cmd_spec_dirstruct (const char *com, const char *val, void *place_ignored) { if (!cmd_boolean (com, val, &opt.dirstruct)) return 0; @@ -891,38 +1062,28 @@ cmd_spec_dirstruct (const char *com, const char *val, void *closure) } static int -cmd_spec_header (const char *com, const char *val, void *closure) +cmd_spec_header (const char *com, const char *val, void *place_ignored) { - if (!*val) + /* Empty value means reset the list of headers. */ + if (*val == '\0') { - /* Empty header means reset headers. */ - FREE_MAYBE (opt.user_header); - opt.user_header = NULL; + free_vec (opt.user_headers); + opt.user_headers = NULL; + return 1; } - else - { - int i; - if (!check_user_specified_header (val)) - { - fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"), - exec_name, com, val); - return 0; - } - i = opt.user_header ? strlen (opt.user_header) : 0; - opt.user_header = (char *)xrealloc (opt.user_header, i + strlen (val) - + 2 + 1); - strcpy (opt.user_header + i, val); - i += strlen (val); - opt.user_header[i++] = '\r'; - opt.user_header[i++] = '\n'; - opt.user_header[i] = '\0'; + if (!check_user_specified_header (val)) + { + fprintf (stderr, _("%s: %s: Invalid header `%s'.\n"), + exec_name, com, val); + return 0; } + opt.user_headers = vec_append (opt.user_headers, val); return 1; } static int -cmd_spec_htmlify (const char *com, const char *val, void *closure) +cmd_spec_htmlify (const char *com, const char *val, void *place_ignored) { int flag = cmd_boolean (com, val, &opt.htmlify); if (flag && !opt.htmlify) @@ -930,8 +1091,11 @@ cmd_spec_htmlify (const char *com, const char *val, void *closure) return flag; } +/* Set the "mirror" mode. It means: recursive download, timestamping, + no limit on max. recursion depth, and don't remove listings. */ + static int -cmd_spec_mirror (const char *com, const char *val, void *closure) +cmd_spec_mirror (const char *com, const char *val, void *place_ignored) { int mirror; @@ -949,8 +1113,29 @@ cmd_spec_mirror (const char *com, const char *val, void *closure) return 1; } +/* Validate --prefer-family and set the choice. Allowed values are + "IPv4", "IPv6", and "none". */ + static int -cmd_spec_progress (const char *com, const char *val, void *closure) +cmd_spec_prefer_family (const char *com, const char *val, void *place_ignored) +{ + static const struct decode_item choices[] = { + { "IPv4", prefer_ipv4 }, + { "IPv6", prefer_ipv6 }, + { "none", prefer_none }, + }; + int ok = decode_string (val, choices, countof (choices), + (int *) &opt.prefer_family); + if (!ok) + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); + return ok; +} + +/* Set progress.type to VAL, but verify that it's a valid progress + implementation before that. */ + +static int +cmd_spec_progress (const char *com, const char *val, void *place_ignored) { if (!valid_progress_implementation_p (val)) { @@ -958,7 +1143,7 @@ cmd_spec_progress (const char *com, const char *val, void *closure) exec_name, com, val); return 0; } - FREE_MAYBE (opt.progress_type); + xfree_null (opt.progress_type); /* Don't call set_progress_implementation here. It will be called in main() when it becomes clear what the log output is. */ @@ -966,8 +1151,12 @@ cmd_spec_progress (const char *com, const char *val, void *closure) return 1; } +/* Set opt.recursive to VAL as with cmd_boolean. If opt.recursive is + set to true, also set opt.dirstruct to 1, unless opt.no_dirstruct + is specified. */ + static int -cmd_spec_recursive (const char *com, const char *val, void *closure) +cmd_spec_recursive (const char *com, const char *val, void *place_ignored) { if (!cmd_boolean (com, val, &opt.recursive)) return 0; @@ -980,39 +1169,200 @@ cmd_spec_recursive (const char *com, const char *val, void *closure) } static int -cmd_spec_useragent (const char *com, const char *val, void *closure) +cmd_spec_restrict_file_names (const char *com, const char *val, void *place_ignored) +{ + int restrict_os = opt.restrict_files_os; + int restrict_ctrl = opt.restrict_files_ctrl; + + const char *end = strchr (val, ','); + if (!end) + end = val + strlen (val); + +#define VAL_IS(string_literal) BOUNDED_EQUAL (val, end, string_literal) + + if (VAL_IS ("unix")) + restrict_os = restrict_unix; + else if (VAL_IS ("windows")) + restrict_os = restrict_windows; + else if (VAL_IS ("nocontrol")) + restrict_ctrl = 0; + else + { + err: + fprintf (stderr, + _("%s: %s: Invalid restriction `%s', use `unix' or `windows'.\n"), + exec_name, com, val); + return 0; + } + +#undef VAL_IS + + if (*end) + { + if (!strcmp (end + 1, "nocontrol")) + restrict_ctrl = 0; + else + goto err; + } + + opt.restrict_files_os = restrict_os; + opt.restrict_files_ctrl = restrict_ctrl; + return 1; +} + +#ifdef HAVE_SSL +static int +cmd_spec_secure_protocol (const char *com, const char *val, void *place) +{ + static const struct decode_item choices[] = { + { "auto", secure_protocol_auto }, + { "sslv2", secure_protocol_sslv2 }, + { "sslv3", secure_protocol_sslv3 }, + { "tlsv1", secure_protocol_tlsv1 }, + }; + int ok = decode_string (val, choices, countof (choices), place); + if (!ok) + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); + return ok; +} +#endif + +/* Set all three timeout values. */ + +static int +cmd_spec_timeout (const char *com, const char *val, void *place_ignored) +{ + double value; + if (!cmd_time (com, val, &value)) + return 0; + opt.read_timeout = value; + opt.connect_timeout = value; + opt.dns_timeout = value; + return 1; +} + +static int +cmd_spec_useragent (const char *com, const char *val, void *place_ignored) { - /* Just check for empty string and newline, so we don't throw total - junk to the server. */ - if (!*val || strchr (val, '\n')) + /* Disallow embedded newlines. */ + if (strchr (val, '\n')) { - fprintf (stderr, _("%s: %s: Invalid specification `%s'.\n"), + fprintf (stderr, _("%s: %s: Invalid value `%s'.\n"), exec_name, com, val); return 0; } + xfree_null (opt.useragent); opt.useragent = xstrdup (val); return 1; } /* Miscellaneous useful routines. */ -/* Return the integer value of a positive integer written in S, or -1 - if an error was encountered. */ +/* A very simple atoi clone, more useful than atoi because it works on + delimited strings, and has error reportage. Returns 1 on success, + 0 on failure. If successful, stores result to *DEST. */ + static int -myatoi (const char *s) +simple_atoi (const char *beg, const char *end, int *dest) { - int res; - const char *orig = s; + int result = 0; + int negative = 0; + const char *p = beg; + + while (p < end && ISSPACE (*p)) + ++p; + if (p < end && (*p == '-' || *p == '+')) + { + negative = (*p == '-'); + ++p; + } + if (p == end) + return 0; - for (res = 0; *s && ISDIGIT (*s); s++) - res = 10 * res + (*s - '0'); - if (*s || orig == s) - return -1; + /* Read negative numbers in a separate loop because the most + negative integer cannot be represented as a positive number. */ + + if (!negative) + for (; p < end && ISDIGIT (*p); p++) + { + int next = (10 * result) + (*p - '0'); + if (next < result) + return 0; /* overflow */ + result = next; + } else - return res; + for (; p < end && ISDIGIT (*p); p++) + { + int next = (10 * result) - (*p - '0'); + if (next > result) + return 0; /* underflow */ + result = next; + } + + if (p != end) + return 0; + + *dest = result; + return 1; } -#define ISODIGIT(x) ((x) >= '0' && (x) <= '7') +/* Trivial atof, with error reporting. Handles "[.]", + doesn't handle exponential notation. Returns 1 on success, 0 on + failure. In case of success, stores its result to *DEST. */ + +static int +simple_atof (const char *beg, const char *end, double *dest) +{ + double result = 0; + + int negative = 0; + int seen_dot = 0; + int seen_digit = 0; + double divider = 1; + + const char *p = beg; + + while (p < end && ISSPACE (*p)) + ++p; + if (p < end && (*p == '-' || *p == '+')) + { + negative = (*p == '-'); + ++p; + } + + for (; p < end; p++) + { + char ch = *p; + if (ISDIGIT (ch)) + { + if (!seen_dot) + result = (10 * result) + (ch - '0'); + else + result += (ch - '0') / (divider *= 10); + seen_digit = 1; + } + else if (ch == '.') + { + if (!seen_dot) + seen_dot = 1; + else + return 0; + } + else + return 0; + } + if (!seen_digit) + return 0; + if (negative) + result = -result; + + *dest = result; + return 1; +} + +/* Verify that the user-specified header in S is valid. It must + contain a colon preceded by non-white-space characters and must not + contain newlines. */ static int check_user_specified_header (const char *s) @@ -1029,11 +1379,26 @@ check_user_specified_header (const char *s) return 0; return 1; } + +/* Decode VAL into a number, according to ITEMS. */ + +static int +decode_string (const char *val, const struct decode_item *items, int itemcount, + int *place) +{ + int i; + for (i = 0; i < itemcount; i++) + if (0 == strcasecmp (val, items[i].name)) + { + *place = items[i].code; + return 1; + } + return 0; +} + -void cleanup_html_url PARAMS ((void)); -void res_cleanup PARAMS ((void)); -void downloaded_files_free PARAMS ((void)); -void http_cleanup PARAMS ((void)); +void cleanup_html_url (void); +void http_cleanup (void); /* Free the memory allocated by global variables. */ @@ -1042,8 +1407,13 @@ cleanup (void) { /* Free external resources, close files, etc. */ - if (opt.dfp) - fclose (opt.dfp); + { + extern FILE *output_stream; + if (output_stream) + fclose (output_stream); + /* No need to check for error because Wget flushes its output (and + checks for errors) after any data arrives. */ + } /* We're exiting anyway so there's no real need to call free() hundreds of times. Skipping the frees will make Wget exit @@ -1054,22 +1424,21 @@ cleanup (void) memory which grows with the size of the program. */ #ifdef DEBUG_MALLOC - recursive_cleanup (); + convert_cleanup (); res_cleanup (); http_cleanup (); cleanup_html_url (); - downloaded_files_free (); host_cleanup (); - cookie_jar_delete (wget_cookie_jar); + log_cleanup (); { extern acc_t *netrc_list; free_netrc (netrc_list); } - FREE_MAYBE (opt.lfilename); - xfree (opt.dir_prefix); - FREE_MAYBE (opt.input_filename); - FREE_MAYBE (opt.output_document); + xfree_null (opt.lfilename); + xfree_null (opt.dir_prefix); + xfree_null (opt.input_filename); + xfree_null (opt.output_document); free_vec (opt.accepts); free_vec (opt.rejects); free_vec (opt.excludes); @@ -1077,24 +1446,30 @@ cleanup (void) free_vec (opt.domains); free_vec (opt.follow_tags); free_vec (opt.ignore_tags); - FREE_MAYBE (opt.progress_type); - xfree (opt.ftp_acc); - FREE_MAYBE (opt.ftp_pass); - FREE_MAYBE (opt.ftp_proxy); - FREE_MAYBE (opt.https_proxy); - FREE_MAYBE (opt.http_proxy); + xfree_null (opt.progress_type); + xfree_null (opt.ftp_user); + xfree_null (opt.ftp_passwd); + xfree_null (opt.ftp_proxy); + xfree_null (opt.https_proxy); + xfree_null (opt.http_proxy); free_vec (opt.no_proxy); - FREE_MAYBE (opt.useragent); - FREE_MAYBE (opt.referer); - FREE_MAYBE (opt.http_user); - FREE_MAYBE (opt.http_passwd); - FREE_MAYBE (opt.user_header); -#ifdef HAVE_SSL - FREE_MAYBE (opt.sslcertkey); - FREE_MAYBE (opt.sslcertfile); -#endif /* HAVE_SSL */ - FREE_MAYBE (opt.bind_address); - FREE_MAYBE (opt.cookies_input); - FREE_MAYBE (opt.cookies_output); -#endif + xfree_null (opt.useragent); + xfree_null (opt.referer); + xfree_null (opt.http_user); + xfree_null (opt.http_passwd); + free_vec (opt.user_headers); +# ifdef HAVE_SSL + xfree_null (opt.cert_file); + xfree_null (opt.private_key); + xfree_null (opt.ca_directory); + xfree_null (opt.ca_cert); + xfree_null (opt.random_file); + xfree_null (opt.egd_file); +# endif + xfree_null (opt.bind_address); + xfree_null (opt.cookies_input); + xfree_null (opt.cookies_output); + xfree_null (opt.user); + xfree_null (opt.passwd); +#endif /* DEBUG_MALLOC */ }