diff --git a/NEWS b/NEWS index cd63c617..7a425eef 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,10 @@ Please send GNU Wget bug reports to . * Changes in Wget 1.11. +** The new function `--ignore-case' makes Wget ignore case when +matching files, directories, and wildcards. This affects the -X, -I, +-A, and -R options, as well as globbing in FTP URLs. + ** ETA projection is now displayed in "dot" progress output as well as in the default progress bar. (The dot progress is used by default when logging Wget's output to file using the `-o' option.) diff --git a/src/ChangeLog b/src/ChangeLog index 8efc71f3..0c1cd1dd 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,13 @@ +2005-07-06 Hrvoje Niksic + + * utils.c (fnmatch_nocase): New function. + (proclist): Use it instead of fnmatch when opt.ignore_case is + requested. + (in_acclist): Respect opt.ignore_case. + (frontcmp): Respect opt.ignore_case. + + * options.h (struct options): New flag opt.ignore_case. + 2005-07-06 Hrvoje Niksic * ptimer.c: Measure time in seconds rather than milliseconds. diff --git a/src/config-post.h b/src/config-post.h index c22df038..f3cbec3c 100644 --- a/src/config-post.h +++ b/src/config-post.h @@ -47,6 +47,11 @@ #define _SVID_SOURCE #define _BSD_SOURCE +/* Under glibc-based systems we want all GNU extensions as well. This + declares some unnecessary cruft, but also useful functions such as + timegm, FNM_CASEFOLD extension to fnmatch, memrchr, etc. */ +#define _GNU_SOURCE + #endif /* NAMESPACE_TWEAKS */ diff --git a/src/ftp.c b/src/ftp.c index 60d9b4a1..dd27349b 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -1621,7 +1621,7 @@ ftp_retrieve_dirs (struct url *u, struct fileinfo *f, ccon *con) DEBUGP (("Composing new CWD relative to the initial directory.\n")); DEBUGP ((" odir = '%s'\n f->name = '%s'\n newdir = '%s'\n\n", odir, f->name, newdir)); - if (!accdir (newdir, ALLABS)) + if (!accdir (newdir)) { logprintf (LOG_VERBOSE, _("\ Not descending to `%s' as it is excluded/not-included.\n"), @@ -1714,12 +1714,14 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action) If we are dealing with a globbing pattern, that is. */ if (*u->file && (action == GLOB_GLOBALL || action == GLOB_GETONE)) { + int (*matcher) (const char *, const char *, int) + = opt.ignore_case ? fnmatch_nocase : fnmatch; int matchres = 0; f = start; while (f) { - matchres = fnmatch (u->file, f->name, 0); + matchres = matcher (u->file, f->name, 0); if (matchres == -1) { logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, diff --git a/src/init.c b/src/init.c index dbad20c3..faffc44c 100644 --- a/src/init.c +++ b/src/init.c @@ -163,6 +163,7 @@ static struct { { "httpproxy", &opt.http_proxy, cmd_string }, { "httpsproxy", &opt.https_proxy, cmd_string }, { "httpuser", &opt.http_user, cmd_string }, + { "ignorecase", &opt.ignore_case, cmd_boolean }, { "ignorelength", &opt.ignore_length, cmd_boolean }, { "ignoretags", &opt.ignore_tags, cmd_vector }, { "includedirectories", &opt.includes, cmd_directory_vector }, diff --git a/src/main.c b/src/main.c index ed334914..6107a51d 100644 --- a/src/main.c +++ b/src/main.c @@ -174,6 +174,7 @@ static struct cmdline_option option_data[] = { "http-passwd", 0, OPT_VALUE, "httppassword", -1 }, /* deprecated */ { "http-password", 0, OPT_VALUE, "httppassword", -1 }, { "http-user", 0, OPT_VALUE, "httpuser", -1 }, + { "ignore-case", 0, OPT_BOOLEAN, "ignorecase", -1 }, { "ignore-length", 0, OPT_BOOLEAN, "ignorelength", -1 }, { "ignore-tags", 0, OPT_VALUE, "ignoretags", -1 }, { "include-directories", 'I', OPT_VALUE, "includedirectories", -1 }, @@ -446,6 +447,8 @@ Download:\n"), --no-dns-cache disable caching DNS lookups.\n"), N_("\ --restrict-file-names=OS restrict chars in file names to ones OS allows.\n"), + N_("\ + --ignore-case ignore case when matching files/directories.\n"), #ifdef ENABLE_IPV6 N_("\ -4, --inet4-only connect only to IPv4 addresses.\n"), diff --git a/src/options.h b/src/options.h index 8c1105ef..8d57d463 100644 --- a/src/options.h +++ b/src/options.h @@ -66,6 +66,8 @@ struct options char **excludes; /* List of excluded FTP directories. */ char **includes; /* List of FTP directories to follow. */ + bool ignore_case; /* Whether to ignore case when + matching dirs and files */ char **domains; /* See host.c */ char **exclude_domains; diff --git a/src/recur.c b/src/recur.c index 6c182f07..71008fb9 100644 --- a/src/recur.c +++ b/src/recur.c @@ -495,7 +495,7 @@ download_child_p (const struct urlpos *upos, struct url *parent, int depth, exclusion and inclusion lists. */ if (opt.includes || opt.excludes) { - if (!accdir (u->dir, ALLABS)) + if (!accdir (u->dir)) { DEBUGP (("%s (%s) is excluded/not-included.\n", url, u->dir)); goto out; diff --git a/src/utils.c b/src/utils.c index 6e9e0a69..991aaf4b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -615,6 +615,28 @@ file_merge (const char *base, const char *file) return result; } +/* Like fnmatch, but performs a lower-case comparison. */ + +int +fnmatch_nocase (const char *pattern, const char *string, int flags) +{ +#ifdef FNM_CASEFOLD + return fnmatch (pattern, string, flags | FNM_CASEFOLD); +#else + /* Turn PATTERN and STRING to lower case and call fnmatch on them. */ + char *patcopy = (char *) alloca (strlen (pattern) + 1); + char *strcopy = (char *) alloca (strlen (string) + 1); + char *p; + for (p = patcopy; *pattern; pattern++, p++) + *p = TOLOWER (*pattern); + *p = '\0'; + for (p = strcopy; *string; string++, p++) + *p = TOLOWER (*string); + *p = '\0'; + return fnmatch (patcopy, strcopy, flags); +#endif +} + static bool in_acclist (const char *const *, const char *, bool); /* Determine whether a file is acceptable to be followed, according to @@ -642,28 +664,34 @@ acceptable (const char *s) } /* Compare S1 and S2 frontally; S2 must begin with S1. E.g. if S1 is - `/something', frontcmp() will return 1 only if S2 begins with - `/something'. Otherwise, 0 is returned. */ + `/something', frontcmp() will return true only if S2 begins with + `/something'. */ bool frontcmp (const char *s1, const char *s2) { - for (; *s1 && *s2 && (*s1 == *s2); ++s1, ++s2); + if (!opt.ignore_case) + for (; *s1 && *s2 && (*s1 == *s2); ++s1, ++s2); + else + for (; *s1 && *s2 && (TOLOWER (*s1) == TOLOWER (*s2)); ++s1, ++s2); return *s1 == '\0'; } /* Iterate through STRLIST, and return the first element that matches S, through wildcards or front comparison (as appropriate). */ static char * -proclist (char **strlist, const char *s, enum accd flags) +proclist (char **strlist, const char *s) { char **x; + int (*matcher) (const char *, const char *, int) + = opt.ignore_case ? fnmatch_nocase : fnmatch; + for (x = strlist; *x; x++) { - /* Remove leading '/' if ALLABS */ - char *p = *x + ((flags & ALLABS) && (**x == '/')); + /* Remove leading '/' */ + char *p = *x + (**x == '/'); if (has_wildcards_p (p)) { - if (fnmatch (p, s, FNM_PATHNAME) == 0) + if (matcher (p, s, FNM_PATHNAME) == 0) break; } else @@ -678,22 +706,23 @@ proclist (char **strlist, const char *s, enum accd flags) /* Returns whether DIRECTORY is acceptable for download, wrt the include/exclude lists. - If FLAGS is ALLABS, the leading `/' is ignored in paths; relative - and absolute paths may be freely intermixed. */ + The leading `/' is ignored in paths; relative and absolute paths + may be freely intermixed. */ + bool -accdir (const char *directory, enum accd flags) +accdir (const char *directory) { /* Remove starting '/'. */ - if (flags & ALLABS && *directory == '/') + if (*directory == '/') ++directory; if (opt.includes) { - if (!proclist (opt.includes, directory, flags)) + if (!proclist (opt.includes, directory)) return false; } if (opt.excludes) { - if (proclist (opt.excludes, directory, flags)) + if (proclist (opt.excludes, directory)) return false; } return true; @@ -748,21 +777,24 @@ in_acclist (const char *const *accepts, const char *s, bool backward) { if (has_wildcards_p (*accepts)) { - /* fnmatch returns 0 if the pattern *does* match the - string. */ - if (fnmatch (*accepts, s, 0) == 0) + int res = opt.ignore_case + ? fnmatch_nocase (*accepts, s, 0) : fnmatch (*accepts, s, 0); + /* fnmatch returns 0 if the pattern *does* match the string. */ + if (res == 0) return true; } else { if (backward) { - if (match_tail (s, *accepts, 0)) + if (match_tail (s, *accepts, opt.ignore_case)) return true; } else { - if (!strcmp (s, *accepts)) + int cmp = opt.ignore_case + ? strcasecmp (s, *accepts) : strcmp (s, *accepts); + if (cmp == 0) return true; } } diff --git a/src/utils.h b/src/utils.h index 2cdb4484..db0edb0f 100644 --- a/src/utils.h +++ b/src/utils.h @@ -30,10 +30,6 @@ so, delete this exception statement from your version. */ #ifndef UTILS_H #define UTILS_H -enum accd { - ALLABS = 1 -}; - struct hash_table; struct file_memory { @@ -72,8 +68,9 @@ FILE *unique_create (const char *, bool, char **); FILE *fopen_excl (const char *, bool); char *file_merge (const char *, const char *); +int fnmatch_nocase (const char *, const char *, int); bool acceptable (const char *); -bool accdir (const char *s, enum accd); +bool accdir (const char *s); char *suffix (const char *s); bool match_tail (const char *, const char *, bool); bool has_wildcards_p (const char *);