X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Finit.c;h=d3d0f9b3efba8c33a7e74fdcbeb00961fab58829;hp=3673d20829b5d74568d88aab3c5e87ba502409e5;hb=e911bc29434b7da90446d2ca5304106724d05680;hpb=12ffc87ca78d3bd8db6a291845604f7c88c1087e diff --git a/src/init.c b/src/init.c index 3673d208..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, 2003 - Free Software Foundation, Inc. + Copyright (C) 2005 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -31,28 +30,13 @@ 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 #endif @@ -62,16 +46,11 @@ so, delete this exception statement from your version. */ #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 @@ -80,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); @@ -99,21 +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 findcmd() 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 }, @@ -123,13 +110,22 @@ 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 }, @@ -143,32 +139,41 @@ static struct { { "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 }, @@ -177,15 +182,27 @@ 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 }, @@ -200,41 +217,38 @@ static struct { { "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 - { "sslcadir", &opt.sslcadir, cmd_directory }, - { "sslcafile", &opt.sslcafile, cmd_file }, - { "sslcertfile", &opt.sslcertfile, cmd_file }, - { "sslcertkey", &opt.sslcertkey, cmd_file }, - { "sslcerttype", &opt.sslcerttype, cmd_number }, - { "sslcheckcert", &opt.sslcheckcert, cmd_number }, - { "sslprotocol", &opt.sslprotocol, cmd_number }, -#endif /* HAVE_SSL */ { "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 -findcmd (const char *com) +command_by_name (const char *cmdname) { + /* 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) @@ -256,16 +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.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; @@ -286,6 +297,11 @@ defaults (void) 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__) @@ -313,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 */ } @@ -346,27 +362,24 @@ wgetrc_file_name (void) 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 */ @@ -380,51 +393,78 @@ wgetrc_file_name (void) return file; } -static int parse_line PARAMS ((const char *, char **, char **, int *)); -static int setval_internal PARAMS ((int, const char *, const char *)); +/* Return values of parse_line. */ +enum parse_line { + line_ok, + line_empty, + line_syntax_error, + line_unknown_command +}; -/* Initialize variables from a wgetrc file. */ +static enum parse_line parse_line (const char *, char **, char **, int *); +static int setval_internal (int, const char *, const char *); -static void +/* 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 comind, status; + char *com = NULL, *val = NULL; + int comind; /* Parse the line. */ - status = parse_line (line, &com, &val, &comind); - xfree (line); - /* If everything is OK, set the value. */ - if (status == 1) + switch (parse_line (line, &com, &val, &comind)) { + 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); - xfree (com); - xfree (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 @@ -433,6 +473,7 @@ void initialize (void) { char *file; + int ok = 1; /* Load the hard-coded defaults. */ defaults (); @@ -440,7 +481,7 @@ 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 (); @@ -457,7 +498,12 @@ 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; } @@ -483,16 +529,14 @@ dehyphen (char *s) 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 - error - -1 - empty + Returns one of line_ok, line_empty, line_syntax_error, or + line_unknown_command. - In case of success, *COM and *VAL point to freshly allocated + 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, those values are unaffected. */ + empty line, their values are unmodified. */ -static int +static enum parse_line parse_line (const char *line, char **com, char **val, int *comind) { const char *p; @@ -511,7 +555,7 @@ parse_line (const char *line, char **com, char **val, int *comind) /* Skip empty lines and comments. */ if (!*line || *line == '#') - return -1; + return line_empty; p = line; @@ -524,7 +568,7 @@ parse_line (const char *line, char **com, char **val, int *comind) while (p < end && ISSPACE (*p)) ++p; if (p == end || *p != '=') - return 0; + return line_syntax_error; ++p; while (p < end && ISSPACE (*p)) ++p; @@ -532,20 +576,22 @@ parse_line (const char *line, char **com, char **val, int *comind) valstart = p; valend = end; + /* 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); + /* The line now known to be syntactically correct. Check whether the command is valid. */ BOUNDED_TO_ALLOCA (cmdstart, cmdend, cmdcopy); dehyphen (cmdcopy); - ind = findcmd (cmdcopy); + ind = command_by_name (cmdcopy); if (ind == -1) - return 0; + return line_unknown_command; - /* The command is valid. Now fill in the values and report success - to the caller. */ + /* Report success to the caller. */ *comind = ind; - *com = strdupdelim (cmdstart, cmdend); - *val = strdupdelim (valstart, valend); - return 1; + return line_ok; } /* Run commands[comind].action. */ @@ -554,21 +600,31 @@ static int setval_internal (int comind, const char *com, const char *val) { assert (0 <= comind && comind < countof (commands)); - return ((*commands[comind].action) (com, val, commands[comind].closure)); + DEBUGP (("Setting %s (%s) to %s\n", com, commands[comind].name, val)); + return commands[comind].action (com, val, commands[comind].place); } /* 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() with commands not - provided by the user, therefore it aborts when an unknown command - is encountered. Once the COMIND's are exported to init.h, this - function will be changed to accept COMIND directly. */ + 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) +setoptval (const char *com, const char *val, const char *optname) { - if (!setval_internal (findcmd (com), com, val)) + /* 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); } @@ -581,16 +637,15 @@ run_command (const char *opt) { char *com, *val; int comind; - int status = parse_line (opt, &com, &val, &comind); - if (status == 1) + switch (parse_line (opt, &com, &val, &comind)) { + case line_ok: if (!setval_internal (comind, com, val)) exit (2); xfree (com); xfree (val); - } - else if (status == 0) - { + break; + default: fprintf (stderr, _("%s: Invalid --execute command `%s'\n"), exec_name, opt); exit (2); @@ -599,6 +654,15 @@ run_command (const char *opt) /* Generic helper functions, for use with `commands'. */ +/* 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 *); + #define CMP1(p, c0) (TOLOWER((p)[0]) == (c0) && (p)[1] == '\0') #define CMP2(p, c0, c1) (TOLOWER((p)[0]) == (c0) \ @@ -611,10 +675,10 @@ run_command (const char *opt) && (p)[3] == '\0') -/* Store the boolean value from VAL to CLOSURE. COM is ignored, +/* 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; @@ -627,66 +691,22 @@ cmd_boolean (const char *com, const char *val, void *closure) else { fprintf (stderr, - _("%s: %s: Invalid boolean `%s', use `on' or `off'.\n"), - exec_name, com, val); - 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; - - int oldval = *(int *)closure; - - /* - * If a config file said "always" or "never", don't allow command line - * arguments to override the config file. - */ - if (oldval == -1 || oldval == 2) - return 1; - - if (0 == strcasecmp (val, "always") || CMP1 (val, '2')) - lockable_boolean_value = 2; - else if (CMP2 (val, 'o', 'n') || CMP3 (val, 'y', 'e', 's') || CMP1 (val, '1')) - lockable_boolean_value = 1; - else if (CMP3 (val, 'o', 'f', 'f') || CMP2 (val, 'n', 'o') || CMP1 (val, '0')) - lockable_boolean_value = 0; - else if (0 == strcasecmp (val, "never") || CMP2 (val, '-', '1')) - lockable_boolean_value = -1; - else - { - fprintf (stderr, - _("%s: %s: Invalid boolean `%s', use always, on, off, or never.\n"), + _("%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; } -static int simple_atoi PARAMS ((const char *, const char *, int *)); - -/* 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) { - if (!simple_atoi (val, val + strlen (val), closure)) + if (!simple_atoi (val, val + strlen (val), place) + || *(int *) place < 0) { fprintf (stderr, _("%s: %s: Invalid number `%s'.\n"), exec_name, com, val); @@ -697,24 +717,24 @@ cmd_number (const char *com, const char *val, void *closure) /* 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; } @@ -729,13 +749,13 @@ cmd_string (const char *com, const char *val, void *closure) `.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 == '~' && ISSEP (val[1]))) { @@ -744,7 +764,6 @@ cmd_file (const char *com, const char *val, void *closure) } else { - char *result; int homelen; char *home = home_dir (); if (!home) @@ -758,12 +777,7 @@ cmd_file (const char *com, const char *val, void *closure) for (++val; ISSEP (*val); val++) ; - result = xmalloc (homelen + 1 + strlen (val) + 1); - memcpy (result, home, homelen); - result[homelen] = '/'; - strcpy (result + homelen + 1, val); - - *pstring = result; + *pstring = concat_strings (home, "/", val, (char *) 0); } #ifdef WINDOWS @@ -780,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'; @@ -799,13 +813,13 @@ cmd_directory (const char *com, const char *val, void *closure) } /* Split VAL by space to a vector of values, and append those values - to vector pointed to by the CLOSURE argument. If VAL is empty, the - CLOSURE vector is cleared instead. */ + 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)); @@ -818,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) { @@ -848,62 +862,45 @@ cmd_directory_vector (const char *com, const char *val, void *closure) return 1; } -static int simple_atof PARAMS ((const char *, const char *, double *)); - -/* Parse VAL as a number and set its value to CLOSURE (which should - point to a long int). - - 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 CLOSURE - remains unmodified. */ +/* Engine for cmd_bytes and cmd_bytes_large: converts a string such as + "100k" or "2.5G" to a floating point number. */ static int -cmd_bytes (const char *com, const char *val, void *closure) +parse_bytes_helper (const char *val, double *result) { - long mult; - double number; + double number, mult; const char *end = val + strlen (val); /* Check for "inf". */ if (0 == strcmp (val, "inf")) { - *(long *)closure = 0; + *result = 0; return 1; } /* Strip trailing whitespace. */ while (val < end && ISSPACE (end[-1])) --end; - if (val == end) - { - err: - fprintf (stderr, _("%s: %s: Invalid byte value `%s'\n"), - exec_name, com, val); - return 0; - } + return 0; switch (TOLOWER (end[-1])) { case 'k': - --end, mult = 1L<<10; + --end, mult = 1024.0; break; case 'm': - --end, mult = 1L<<20; + --end, mult = 1048576.0; break; case 'g': - --end, mult = 1L<<30; + --end, mult = 1073741824.0; + break; + case 't': + --end, mult = 1099511627776.0; break; default: - /* Not a recognized suffix: assume it belongs to the number. - (If not, atof simple_atof will raise an error.) */ + /* Not a recognized suffix: assume it's a digit. (If not, + simple_atof will raise an error.) */ mult = 1; } @@ -913,12 +910,59 @@ cmd_bytes (const char *com, const char *val, void *closure) while (val < end && ISSPACE (end[-1])) --end; if (val == end) - goto err; + return 0; - if (!simple_atof (val, end, &number)) - goto err; + 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; + } + *(wgint *)place = (wgint)byte_value; + return 1; +} - *(long *)closure = (long)(number * mult); +/* 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_bytes_large (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; + } + *(LARGE_INT *)place = (LARGE_INT)byte_value; return 1; } @@ -927,7 +971,7 @@ cmd_bytes (const char *com, const char *val, void *closure) "d", and "w" for minutes, hours, days, and weeks respectively. */ static int -cmd_time (const char *com, const char *val, void *closure) +cmd_time (const char *com, const char *val, void *place) { double number, mult; const char *end = val + strlen (val); @@ -963,7 +1007,7 @@ cmd_time (const char *com, const char *val, void *closure) break; default: /* Not a recognized suffix: assume it belongs to the number. - (If not, atof simple_atof will raise an error.) */ + (If not, simple_atof will raise an error.) */ mult = 1; } @@ -978,17 +1022,33 @@ cmd_time (const char *com, const char *val, void *closure) if (!simple_atof (val, end, &number)) goto err; - *(double *)closure = number * mult; + *(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; @@ -1002,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 header `%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) @@ -1045,7 +1095,7 @@ cmd_spec_htmlify (const char *com, const char *val, void *closure) 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; @@ -1063,11 +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_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 *closure) +cmd_spec_progress (const char *com, const char *val, void *place_ignored) { if (!valid_progress_implementation_p (val)) { @@ -1075,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. */ @@ -1088,7 +1156,7 @@ cmd_spec_progress (const char *com, const char *val, void *closure) 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; @@ -1101,7 +1169,7 @@ cmd_spec_recursive (const char *com, const char *val, void *closure) } static int -cmd_spec_restrict_file_names (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; @@ -1142,10 +1210,27 @@ cmd_spec_restrict_file_names (const char *com, const char *val, void *closure) 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 *closure) +cmd_spec_timeout (const char *com, const char *val, void *place_ignored) { double value; if (!cmd_time (com, val, &value)) @@ -1157,37 +1242,62 @@ cmd_spec_timeout (const char *com, const char *val, void *closure) } static int -cmd_spec_useragent (const char *com, const char *val, void *closure) +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 value `%s'.\n"), exec_name, com, val); return 0; } + xfree_null (opt.useragent); opt.useragent = xstrdup (val); return 1; } /* Miscellaneous useful routines. */ -/* A very simple atoi clone, more portable than strtol and friends, - but reports errors, unlike atoi. Returns 1 on success, 0 on - failure. In case of success, stores result to *DEST. */ +/* 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 simple_atoi (const char *beg, const char *end, int *dest) { int result = 0; - const char *p; + int negative = 0; + const char *p = beg; - if (beg == end) + while (p < end && ISSPACE (*p)) + ++p; + if (p < end && (*p == '-' || *p == '+')) + { + negative = (*p == '-'); + ++p; + } + if (p == end) return 0; - for (p = beg; p < end && ISDIGIT (*p); p++) - result = (10 * result) + (*p - '0'); + /* 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 + 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; @@ -1205,13 +1315,22 @@ 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; + const char *p = beg; + + while (p < end && ISSPACE (*p)) + ++p; + if (p < end && (*p == '-' || *p == '+')) + { + negative = (*p == '-'); + ++p; + } - for (p = beg; p < end; p++) + for (; p < end; p++) { char ch = *p; if (ISDIGIT (ch)) @@ -1234,11 +1353,17 @@ simple_atof (const char *beg, const char *end, double *dest) } 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) { @@ -1254,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. */ @@ -1267,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 @@ -1279,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); - FREE_MAYBE (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); @@ -1302,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 */ }