]> sjero.net Git - wget/blobdiff - src/utils.c
Fix build when libpsl is not available
[wget] / src / utils.c
index 90b50043c59772253924e49fce99b115f8dbe092..8168fb38719d8880d08ef53608a0a31afe86720c 100644 (file)
@@ -1,5 +1,7 @@
 /* Various utility functions.
-   Copyright (C) 1996-2006 Free Software Foundation, Inc.
+   Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+   2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation,
+   Inc.
 
 This file is part of GNU Wget.
 
@@ -16,36 +18,29 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
 
-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.  */
+Additional permission under GNU GPL version 3 section 7
 
-#include <config.h>
+If you modify this program, or any covered work, by linking or
+combining it with the OpenSSL project's OpenSSL library (or a
+modified version of that library), containing parts covered by the
+terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
+grants you additional permission to convey the resulting work.
+Corresponding Source for a non-source form of such a combination
+shall include the source code for the parts of OpenSSL used as well
+as that of the covered work.  */
+
+#include "wget.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
-#ifdef HAVE_SYS_TIME_H
-# include <sys/time.h>
-#endif
-#ifdef HAVE_UNISTD_H
-# include <unistd.h>
-#endif
+#include <unistd.h>
 #ifdef HAVE_MMAP
 # include <sys/mman.h>
 #endif
-#ifdef HAVE_UTIME_H
-# include <utime.h>
-#endif
-#ifdef HAVE_SYS_UTIME_H
-# include <sys/utime.h>
+#ifdef HAVE_PROCESS_H
+# include <process.h>  /* getpid() */
 #endif
 #include <errno.h>
 #include <fcntl.h>
@@ -53,10 +48,23 @@ so, delete this exception statement from your version.  */
 #include <stdarg.h>
 #include <locale.h>
 
-/* For TIOCGWINSZ and friends: */
-#ifdef HAVE_SYS_IOCTL_H
-# include <sys/ioctl.h>
+#if HAVE_UTIME
+# include <sys/types.h>
+# ifdef HAVE_UTIME_H
+#  include <utime.h>
+# endif
+
+# ifdef HAVE_SYS_UTIME_H
+#  include <sys/utime.h>
+# endif
 #endif
+
+#include <sys/time.h>
+
+#include <sys/stat.h>
+
+/* For TIOCGWINSZ and friends: */
+#include <sys/ioctl.h>
 #ifdef HAVE_TERMIOS_H
 # include <termios.h>
 #endif
@@ -65,6 +73,11 @@ so, delete this exception statement from your version.  */
 #include <signal.h>
 #include <setjmp.h>
 
+#include <regex.h>
+#ifdef HAVE_LIBPCRE
+# include <pcre.h>
+#endif
+
 #ifndef HAVE_SIGSETJMP
 /* If sigsetjmp is a macro, configure won't pick it up. */
 # ifdef sigsetjmp
@@ -76,13 +89,124 @@ so, delete this exception statement from your version.  */
 # define USE_SIGNAL_TIMEOUT
 #endif
 
-#include "wget.h"
 #include "utils.h"
 #include "hash.h"
 
+#ifdef __VMS
+#include "vms.h"
+#endif /* def __VMS */
+
 #ifdef TESTING
 #include "test.h"
-#endif 
+#endif
+
+static void
+memfatal (const char *context, long attempted_size)
+{
+  /* Make sure we don't try to store part of the log line, and thus
+     call malloc.  */
+  log_set_save_context (false);
+
+  /* We have different log outputs in different situations:
+     1) output without bytes information
+     2) output with bytes information  */
+  if (attempted_size == UNKNOWN_ATTEMPTED_SIZE)
+    {
+      logprintf (LOG_ALWAYS,
+                 _("%s: %s: Failed to allocate enough memory; memory exhausted.\n"),
+                 exec_name, context);
+    }
+  else
+    {
+      logprintf (LOG_ALWAYS,
+                 _("%s: %s: Failed to allocate %ld bytes; memory exhausted.\n"),
+                 exec_name, context, attempted_size);
+    }
+
+  exit (1);
+}
+
+/* Character property table for (re-)escaping VMS ODS5 extended file
+   names.  Note that this table ignores Unicode.
+
+   ODS2 valid characters: 0-9 A-Z a-z $ - _ ~
+
+   ODS5 Invalid characters:
+      C0 control codes (0x00 to 0x1F inclusive)
+      Asterisk (*)
+      Question mark (?)
+
+   ODS5 Invalid characters only in VMS V7.2 (which no one runs, right?):
+      Double quotation marks (")
+      Backslash (\)
+      Colon (:)
+      Left angle bracket (<)
+      Right angle bracket (>)
+      Slash (/)
+      Vertical bar (|)
+
+   Characters escaped by "^":
+      SP  !  "  #  %  &  '  (  )  +  ,  .  :  ;  =
+       @  [  \  ]  ^  `  {  |  }  ~
+
+   Either "^_" or "^ " is accepted as a space.  Period (.) is a special
+   case.  Note that un-escaped < and > can also confuse a directory
+   spec.
+
+   Characters put out as ^xx:
+      7F (DEL)
+      80-9F (C1 control characters)
+      A0 (nonbreaking space)
+      FF (Latin small letter y diaeresis)
+
+   Other cases:
+      Unicode: "^Uxxxx", where "xxxx" is four hex digits.
+
+    Property table values:
+      Normal escape:    1
+      Space:            2
+      Dot:              4
+      Hex-hex escape:   8
+      ODS2 normal:     16
+      ODS2 lower case: 32
+      Hex digit:       64
+*/
+
+unsigned char char_prop[ 256] = {
+
+/* NUL SOH STX ETX EOT ENQ ACK BEL   BS  HT  LF  VT  FF  CR  SO  SI */
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+
+/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB  CAN  EM SUB ESC  FS  GS  RS  US */
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+
+/*  SP  !   "   #   $   %   &   '    (   )   *   +   ,   -   .   /  */
+    2,  1,  1,  1, 16,  1,  1,  1,   1,  1,  0,  1,  1, 16,  4,  0,
+
+/*  0   1   2   3   4   5   6   7    8   9   :   ;   <   =   >   ?  */
+   80, 80, 80, 80, 80, 80, 80, 80,  80, 80,  1,  1,  1,  1,  1,  1,
+
+/*  @   A   B   C   D   E   F   G    H   I   J   K   L   M   N   O  */
+    1, 80, 80, 80, 80, 80, 80, 16,  16, 16, 16, 16, 16, 16, 16, 16,
+
+/*  P   Q   R   S   T   U   V   W    X   Y   Z   [   \   ]   ^   _  */
+   16, 16, 16, 16, 16, 16, 16, 16,  16, 16, 16,  1,  1,  1,  1, 16,
+
+/*  `   a   b   c   d   e   f   g    h   i   j   k   l   m   n   o  */
+    1, 96, 96, 96, 96, 96, 96, 32,  32, 32, 32, 32, 32, 32, 32, 32,
+
+/*  p   q   r   s   t   u   v   w    x   y   z   {   |   }   ~  DEL */
+   32, 32, 32, 32, 32, 32, 32, 32,  32, 32, 32,  1,  1,  1, 17,  8,
+
+    8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
+    8,  8,  8,  8,  8,  8,  8,  8,   8,  8,  8,  8,  8,  8,  8,  8,
+    8,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  0,
+    0,  0,  0,  0,  0,  0,  0,  0,   0,  0,  0,  0,  0,  0,  0,  8
+};
 
 /* Utility function: like xstrdup(), but also lowercases S.  */
 
@@ -92,7 +216,7 @@ xstrdup_lower (const char *s)
   char *copy = xstrdup (s);
   char *p = copy;
   for (; *p; p++)
-    *p = TOLOWER (*p);
+    *p = c_tolower (*p);
   return copy;
 }
 
@@ -125,18 +249,18 @@ sepstring (const char *s)
   while (*s)
     {
       if (*s == ',')
-       {
-         res = xrealloc (res, (i + 2) * sizeof (char *));
-         res[i] = strdupdelim (p, s);
-         res[++i] = NULL;
-         ++s;
-         /* Skip the blanks following the ','.  */
-         while (ISSPACE (*s))
-           ++s;
-         p = s;
-       }
+        {
+          res = xrealloc (res, (i + 2) * sizeof (char *));
+          res[i] = strdupdelim (p, s);
+          res[++i] = NULL;
+          ++s;
+          /* Skip the blanks following the ','.  */
+          while (c_isspace (*s))
+            ++s;
+          p = s;
+        }
       else
-       ++s;
+        ++s;
     }
   res = xrealloc (res, (i + 2) * sizeof (char *));
   res[i] = strdupdelim (p, s);
@@ -155,6 +279,13 @@ sepstring (const char *s)
    vsnprintf until the correct size is found.  Since Wget also ships a
    fallback implementation of vsnprintf, this should be portable.  */
 
+/* Constant is using for limits memory allocation for text buffer.
+   Applicable in situation when: vasprintf is not available in the system
+   and vsnprintf return -1 when long line is truncated (in old versions of
+   glibc and in other system where C99 doesn`t support) */
+
+#define FMT_MAX_LENGTH 1048576
+
 char *
 aprintf (const char *fmt, ...)
 {
@@ -167,7 +298,8 @@ aprintf (const char *fmt, ...)
   ret = vasprintf (&str, fmt, args);
   va_end (args);
   if (ret < 0 && errno == ENOMEM)
-    abort ();                  /* for consistency with xmalloc/xrealloc */
+    memfatal ("aprintf", UNKNOWN_ATTEMPTED_SIZE);  /* for consistency
+                                                      with xmalloc/xrealloc */
   else if (ret < 0)
     return NULL;
   return str;
@@ -192,13 +324,26 @@ aprintf (const char *fmt, ...)
 
       /* If the printing worked, return the string. */
       if (n > -1 && n < size)
-       return str;
+        return str;
 
       /* Else try again with a larger buffer. */
-      if (n > -1)              /* C99 */
-       size = n + 1;           /* precisely what is needed */
+      if (n > -1)               /* C99 */
+        size = n + 1;           /* precisely what is needed */
+      else if (size >= FMT_MAX_LENGTH)  /* We have a huge buffer, */
+        {                               /* maybe we have some wrong
+                                           format string? */
+          logprintf (LOG_ALWAYS,
+                     _("%s: aprintf: text buffer is too big (%ld bytes), "
+                       "aborting.\n"),
+                     exec_name, size);  /* printout a log message */
+          abort ();                     /* and abort... */
+        }
       else
-       size <<= 1;             /* twice the old size */
+        {
+          /* else, we continue to grow our
+           * buffer: Twice the old size. */
+          size <<= 1;
+        }
       str = xrealloc (str, size);
     }
 #endif /* not HAVE_VASPRINTF */
@@ -211,12 +356,12 @@ char *
 concat_strings (const char *str0, ...)
 {
   va_list args;
-  int saved_lengths[5];                /* inspired by Apache's apr_pstrcat */
+  int saved_lengths[5];         /* inspired by Apache's apr_pstrcat */
   char *ret, *p;
 
   const char *next_str;
   int total_length = 0;
-  int argcount;
+  size_t argcount;
 
   /* Calculate the length of and allocate the resulting string. */
 
@@ -226,7 +371,7 @@ concat_strings (const char *str0, ...)
     {
       int len = strlen (next_str);
       if (argcount < countof (saved_lengths))
-       saved_lengths[argcount++] = len;
+        saved_lengths[argcount++] = len;
       total_length += len;
     }
   va_end (args);
@@ -240,9 +385,9 @@ concat_strings (const char *str0, ...)
     {
       int len;
       if (argcount < countof (saved_lengths))
-       len = saved_lengths[argcount++];
+        len = saved_lengths[argcount++];
       else
-       len = strlen (next_str);
+        len = strlen (next_str);
       memcpy (p, next_str, len);
       p += len;
     }
@@ -287,9 +432,19 @@ datetime_str (time_t t)
 }
 \f
 /* The Windows versions of the following two functions are defined in
-   mswindows.c.  */
+   mswindows.c. On MSDOS this function should never be called. */
 
-#ifndef WINDOWS
+#ifdef __VMS
+
+void
+fork_to_background (void)
+{
+  return;
+}
+
+#else /* def __VMS */
+
+#if !defined(WINDOWS) && !defined(MSDOS)
 void
 fork_to_background (void)
 {
@@ -297,19 +452,19 @@ fork_to_background (void)
   /* Whether we arrange our own version of opt.lfilename here.  */
   bool logfile_changed = false;
 
-  if (!opt.lfilename)
+  if (!opt.lfilename && (!opt.quiet || opt.server_response))
     {
       /* We must create the file immediately to avoid either a race
-        condition (which arises from using unique_name and failing to
-        use fopen_excl) or lying to the user about the log file name
-        (which arises from using unique_name, printing the name, and
-        using fopen_excl later on.)  */
+         condition (which arises from using unique_name and failing to
+         use fopen_excl) or lying to the user about the log file name
+         (which arises from using unique_name, printing the name, and
+         using fopen_excl later on.)  */
       FILE *new_log_fp = unique_create (DEFAULT_LOGFILE, false, &opt.lfilename);
       if (new_log_fp)
-       {
-         logfile_changed = true;
-         fclose (new_log_fp);
-       }
+        {
+          logfile_changed = true;
+          fclose (new_log_fp);
+        }
     }
   pid = fork ();
   if (pid < 0)
@@ -323,17 +478,23 @@ fork_to_background (void)
       /* parent, no error */
       printf (_("Continuing in background, pid %d.\n"), (int) pid);
       if (logfile_changed)
-       printf (_("Output will be written to `%s'.\n"), opt.lfilename);
-      exit (0);                        /* #### should we use _exit()? */
+        printf (_("Output will be written to %s.\n"), quote (opt.lfilename));
+      exit (0);                 /* #### should we use _exit()? */
     }
 
   /* child: give up the privileges and keep running. */
   setsid ();
-  freopen ("/dev/null", "r", stdin);
-  freopen ("/dev/null", "w", stdout);
-  freopen ("/dev/null", "w", stderr);
+  if (freopen ("/dev/null", "r", stdin) == NULL)
+    DEBUGP (("Failed to redirect stdin to /dev/null.\n"));
+  if (freopen ("/dev/null", "w", stdout) == NULL)
+    DEBUGP (("Failed to redirect stdout to /dev/null.\n"));
+  if (freopen ("/dev/null", "w", stderr) == NULL)
+    DEBUGP (("Failed to redirect stderr to /dev/null.\n"));
 }
-#endif /* not WINDOWS */
+#endif /* !WINDOWS && !MSDOS */
+
+#endif /* def __VMS [else] */
+
 \f
 /* "Touch" FILE, i.e. make its mtime ("modified time") equal the time
    specified with TM.  The atime ("access time") is set to the current
@@ -342,18 +503,40 @@ fork_to_background (void)
 void
 touch (const char *file, time_t tm)
 {
-#ifdef HAVE_STRUCT_UTIMBUF
+#if HAVE_UTIME
+# ifdef HAVE_STRUCT_UTIMBUF
   struct utimbuf times;
-#else
+# else
   struct {
     time_t actime;
     time_t modtime;
   } times;
-#endif
+# endif
   times.modtime = tm;
   times.actime = time (NULL);
   if (utime (file, &times) == -1)
     logprintf (LOG_NOTQUIET, "utime(%s): %s\n", file, strerror (errno));
+#else
+  struct timespec timespecs[2];
+  int fd;
+
+  fd = open (file, O_WRONLY);
+  if (fd < 0)
+    {
+      logprintf (LOG_NOTQUIET, "open(%s): %s\n", file, strerror (errno));
+      return;
+    }
+
+  timespecs[0].tv_sec = time (NULL);
+  timespecs[0].tv_nsec = 0L;
+  timespecs[1].tv_sec = tm;
+  timespecs[1].tv_nsec = 0L;
+
+  if (futimens (fd, timespecs) == -1)
+    logprintf (LOG_NOTQUIET, "futimens(%s): %s\n", file, strerror (errno));
+
+  close (fd);
+#endif
 }
 
 /* Checks if FILE is a symbolic link, and removes it if it is.  Does
@@ -369,8 +552,8 @@ remove_link (const char *file)
       DEBUGP (("Unlinking %s (symlink).\n", file));
       err = unlink (file);
       if (err != 0)
-       logprintf (LOG_VERBOSE, _("Failed to unlink symlink `%s': %s\n"),
-                  file, strerror (errno));
+        logprintf (LOG_VERBOSE, _("Failed to unlink symlink %s: %s\n"),
+                   quote (file), strerror (errno));
     }
   return err;
 }
@@ -432,6 +615,14 @@ file_size (const char *filename)
 #endif
 }
 
+/* 2005-02-19 SMS.
+   If no UNIQ_SEP is defined (as on VMS), have unique_name() return the
+   original name.  With the VMS file systems' versioning, everything
+   should be fine, and appending ".NN" just causes trouble.
+*/
+
+#ifdef UNIQ_SEP
+
 /* stat file names named PREFIX.1, PREFIX.2, etc., until one that
    doesn't exist is found.  Return a freshly allocated copy of the
    unused file name.  */
@@ -445,7 +636,7 @@ unique_name_1 (const char *prefix)
   char *template_tail = template + plen;
 
   memcpy (template, prefix, plen);
-  *template_tail++ = '.';
+  *template_tail++ = UNIQ_SEP;
 
   do
     number_to_string (template_tail, count++);
@@ -460,6 +651,8 @@ unique_name_1 (const char *prefix)
    If not, FILE.1 is tried, then FILE.2, etc.  The first FILE.<number>
    file name that doesn't exist is returned.
 
+   2005-02-19 SMS.  "." is now UNIQ_SEP, and may be different.
+
    The resulting file is not created, only verified that it didn't
    exist at the point in time when the function was called.
    Therefore, where security matters, don't rely that the file created
@@ -483,6 +676,20 @@ unique_name (const char *file, bool allow_passthrough)
   return unique_name_1 (file);
 }
 
+#else /* def UNIQ_SEP */
+
+/* Dummy unique_name() for VMS.  Return the original name as easily as
+   possible.
+*/
+char *
+unique_name (const char *file, bool allow_passthrough)
+{
+  /* Return the FILE itself, without modification, irregardful. */
+  return allow_passthrough ? (char *)file : xstrdup (file);
+}
+
+#endif /* def UNIQ_SEP [else] */
+
 /* Create a file based on NAME, except without overwriting an existing
    file with that name.  Providing O_EXCL is correctly implemented,
    this function does not have the race condition associated with
@@ -499,15 +706,15 @@ unique_create (const char *name, bool binary, char **opened_name)
       xfree (uname);
       uname = unique_name (name, false);
     }
-  if (opened_name && fp != NULL)
+  if (opened_name)
     {
       if (fp)
-       *opened_name = uname;
+        *opened_name = uname;
       else
-       {
-         *opened_name = NULL;
-         xfree (uname);
-       }
+        {
+          *opened_name = NULL;
+          xfree (uname);
+        }
     }
   else
     xfree (uname);
@@ -523,18 +730,66 @@ unique_create (const char *name, bool binary, char **opened_name)
    If opening the file fails for any reason, including the file having
    previously existed, this function returns NULL and sets errno
    appropriately.  */
-   
+
 FILE *
-fopen_excl (const char *fname, bool binary)
+fopen_excl (const char *fname, int binary)
 {
   int fd;
 #ifdef O_EXCL
+
+/* 2005-04-14 SMS.
+   VMS lacks O_BINARY, but makes up for it in weird and wonderful ways.
+   It also has file versions which obviate all the O_EXCL effort.
+   O_TRUNC (something of a misnomer) requests a new version.
+*/
+# ifdef __VMS
+/* Common open() optional arguments:
+   sequential access only, access callback function.
+*/
+#  define OPEN_OPT_ARGS "fop=sqo", "acc", acc_cb, &open_id
+
+  int open_id;
+  int flags = O_WRONLY | O_CREAT | O_TRUNC;
+
+  if (binary > 1)
+    {
+      open_id = 11;
+      fd = open( fname,                 /* File name. */
+       flags,                           /* Flags. */
+       0777,                            /* Mode for default protection. */
+       "ctx=bin,stm",                   /* Binary, stream access. */
+       "rfm=stmlf",                     /* Stream_LF. */
+       OPEN_OPT_ARGS);                  /* Access callback. */
+    }
+  else if (binary)
+    {
+      open_id = 12;
+      fd = open( fname,                 /* File name. */
+       flags,                           /* Flags. */
+       0777,                            /* Mode for default protection. */
+       "ctx=bin,stm",                   /* Binary, stream access. */
+       "rfm=fix",                       /* Fixed-length, */
+       "mrs=512",                       /* 512-byte records. */
+       OPEN_OPT_ARGS);                  /* Access callback. */
+    }
+  else
+    {
+      open_id = 13;
+      fd = open( fname,                 /* File name. */
+       flags,                           /* Flags. */
+       0777,                            /* Mode for default protection. */
+       "rfm=stmlf",                     /* Stream_LF. */
+       OPEN_OPT_ARGS);                  /* Access callback. */
+    }
+# else /* def __VMS */
   int flags = O_WRONLY | O_CREAT | O_EXCL;
 # ifdef O_BINARY
   if (binary)
     flags |= O_BINARY;
 # endif
   fd = open (fname, flags, 0666);
+# endif /* def __VMS [else] */
+
   if (fd < 0)
     return NULL;
   return fdopen (fd, binary ? "wb" : "w");
@@ -572,21 +827,21 @@ make_directory (const char *directory)
   for (i = (*dir == '/'); 1; ++i)
     {
       for (; dir[i] && dir[i] != '/'; i++)
-       ;
+        ;
       if (!dir[i])
-       quit = 1;
+        quit = 1;
       dir[i] = '\0';
       /* Check whether the directory already exists.  Allow creation of
-        of intermediate directories to fail, as the initial path components
-        are not necessarily directories!  */
+         of intermediate directories to fail, as the initial path components
+         are not necessarily directories!  */
       if (!file_exists_p (dir))
-       ret = mkdir (dir, 0777);
+        ret = mkdir (dir, 0777);
       else
-       ret = 0;
+        ret = 0;
       if (quit)
-       break;
+        break;
       else
-       dir[i] = '/';
+        dir[i] = '/';
     }
   return ret;
 }
@@ -632,10 +887,10 @@ fnmatch_nocase (const char *pattern, const char *string, int flags)
   char *strcopy = (char *) alloca (strlen (string) + 1);
   char *p;
   for (p = patcopy; *pattern; pattern++, p++)
-    *p = TOLOWER (*pattern);
+    *p = c_tolower (*pattern);
   *p = '\0';
   for (p = strcopy; *string; string++, p++)
-    *p = TOLOWER (*string);
+    *p = c_tolower (*string);
   *p = '\0';
   return fnmatch (patcopy, strcopy, flags);
 #endif
@@ -648,38 +903,56 @@ static bool in_acclist (const char *const *, const char *, bool);
 bool
 acceptable (const char *s)
 {
-  int l = strlen (s);
+  const char *p;
+
+  if (opt.output_document && strcmp (s, opt.output_document) == 0)
+    return true;
+
+  if ((p = strrchr (s, '/')))
+    s = p + 1;
 
-  while (l && s[l] != '/')
-    --l;
-  if (s[l] == '/')
-    s += (l + 1);
   if (opt.accepts)
     {
       if (opt.rejects)
-       return (in_acclist ((const char *const *)opt.accepts, s, true)
-               && !in_acclist ((const char *const *)opt.rejects, s, true));
+        return (in_acclist ((const char *const *)opt.accepts, s, true)
+                && !in_acclist ((const char *const *)opt.rejects, s, true));
       else
-       return in_acclist ((const char *const *)opt.accepts, s, true);
+        return in_acclist ((const char *const *)opt.accepts, s, true);
     }
   else if (opt.rejects)
     return !in_acclist ((const char *const *)opt.rejects, s, true);
+
+  return true;
+}
+
+/* Determine whether an URL is acceptable to be followed, according to
+   regex patterns to accept/reject.  */
+bool
+accept_url (const char *s)
+{
+  if (opt.acceptregex && !opt.regex_match_fun (opt.acceptregex, s))
+    return false;
+  if (opt.rejectregex && opt.regex_match_fun (opt.rejectregex, s))
+    return false;
+
   return true;
 }
 
 /* Check if D2 is a subdirectory of D1.  E.g. if D1 is `/something', subdir_p()
-   will return true if and only if D2 begins with `/something/' or is exactly 
+   will return true if and only if D2 begins with `/something/' or is exactly
    '/something'.  */
 bool
 subdir_p (const char *d1, const char *d2)
 {
+  if (*d1 == '\0')
+    return true;
   if (!opt.ignore_case)
     for (; *d1 && *d2 && (*d1 == *d2); ++d1, ++d2)
       ;
   else
-    for (; *d1 && *d2 && (TOLOWER (*d1) == TOLOWER (*d2)); ++d1, ++d2)
+    for (; *d1 && *d2 && (c_tolower (*d1) == c_tolower (*d2)); ++d1, ++d2)
       ;
-  
+
   return *d1 == '\0' && (*d2 == '\0' || *d2 == '/');
 }
 
@@ -687,28 +960,28 @@ subdir_p (const char *d1, const char *d2)
    first element that matches DIR, through wildcards or front comparison (as
    appropriate).  */
 static bool
-dir_matches_p (char **dirlist, const char *dir)
+dir_matches_p (const char **dirlist, const char *dir)
 {
-  char **x;
+  const char **x;
   int (*matcher) (const char *, const char *, int)
     = opt.ignore_case ? fnmatch_nocase : fnmatch;
 
   for (x = dirlist; *x; x++)
     {
       /* Remove leading '/' */
-      char *p = *x + (**x == '/');
+      const char *p = *x + (**x == '/');
       if (has_wildcards_p (p))
-       {
-         if (matcher (p, dir, FNM_PATHNAME) == 0)
-           break;
-       }
+        {
+          if (matcher (p, dir, FNM_PATHNAME) == 0)
+            break;
+        }
       else
-       {
-         if (subdir_p (p, dir))
-           break;
-       }
+        {
+          if (subdir_p (p, dir))
+            break;
+        }
     }
-      
+
   return *x ? true : false;
 }
 
@@ -727,12 +1000,12 @@ accdir (const char *directory)
   if (opt.includes)
     {
       if (!dir_matches_p (opt.includes, directory))
-       return false;
+        return false;
     }
   if (opt.excludes)
     {
       if (dir_matches_p (opt.excludes, directory))
-       return false;
+        return false;
     }
   return true;
 }
@@ -748,29 +1021,15 @@ accdir (const char *directory)
 bool
 match_tail (const char *string, const char *tail, bool fold_case)
 {
-  int i, j;
+  int pos = strlen (string) - strlen (tail);
 
-  /* We want this to be fast, so we code two loops, one with
-     case-folding, one without. */
+  if (pos < 0)
+    return false;  /* tail is longer than string.  */
 
   if (!fold_case)
-    {
-      for (i = strlen (string), j = strlen (tail); i >= 0 && j >= 0; i--, j--)
-       if (string[i] != tail[j])
-         break;
-    }
+    return !strcmp (string + pos, tail);
   else
-    {
-      for (i = strlen (string), j = strlen (tail); i >= 0 && j >= 0; i--, j--)
-       if (TOLOWER (string[i]) != TOLOWER (tail[j]))
-         break;
-    }
-
-  /* If the tail was exhausted, the match was succesful.  */
-  if (j == -1)
-    return true;
-  else
-    return false;
+    return !strcasecmp (string + pos, tail);
 }
 
 /* Checks whether string S matches each element of ACCEPTS.  A list
@@ -785,28 +1044,28 @@ in_acclist (const char *const *accepts, const char *s, bool backward)
   for (; *accepts; accepts++)
     {
       if (has_wildcards_p (*accepts))
-       {
-         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;
-       }
+        {
+          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, opt.ignore_case))
-               return true;
-           }
-         else
-           {
-             int cmp = opt.ignore_case
-               ? strcasecmp (s, *accepts) : strcmp (s, *accepts);
-             if (cmp == 0)
-               return true;
-           }
-       }
+        {
+          if (backward)
+            {
+              if (match_tail (s, *accepts, opt.ignore_case))
+                return true;
+            }
+          else
+            {
+              int cmp = opt.ignore_case
+                ? strcasecmp (s, *accepts) : strcmp (s, *accepts);
+              if (cmp == 0)
+                return true;
+            }
+        }
     }
   return false;
 }
@@ -819,15 +1078,12 @@ in_acclist (const char *const *accepts, const char *s, bool backward)
 char *
 suffix (const char *str)
 {
-  int i;
+  char *p;
 
-  for (i = strlen (str); i && str[i] != '/' && str[i] != '.'; i--)
-    ;
+  if ((p = strrchr (str, '.')) && !strchr (p + 1, '/'))
+    return p + 1;
 
-  if (str[i++] == '.')
-    return (char *)str + i;
-  else
-    return NULL;
+  return NULL;
 }
 
 /* Return true if S contains globbing wildcards (`*', `?', `[' or
@@ -836,16 +1092,13 @@ suffix (const char *str)
 bool
 has_wildcards_p (const char *s)
 {
-  for (; *s; s++)
-    if (*s == '*' || *s == '?' || *s == '[' || *s == ']')
-      return true;
-  return false;
+  return !!strpbrk (s, "*?[]");
 }
 
 /* Return true if FNAME ends with a typical HTML suffix.  The
    following (case-insensitive) suffixes are presumed to be HTML
    files:
-   
+
      html
      htm
      ?html (`?' matches one character)
@@ -868,74 +1121,24 @@ has_html_suffix_p (const char *fname)
   return false;
 }
 
-/* Read a line from FP and return the pointer to freshly allocated
-   storage.  The storage space is obtained through malloc() and should
-   be freed with free() when it is no longer needed.
-
-   The length of the line is not limited, except by available memory.
-   The newline character at the end of line is retained.  The line is
-   terminated with a zero character.
-
-   After end-of-file is encountered without anything being read, NULL
-   is returned.  NULL is also returned on error.  To distinguish
-   between these two cases, use the stdio function ferror().  */
-
-char *
-read_whole_line (FILE *fp)
-{
-  int length = 0;
-  int bufsize = 82;
-  char *line = xmalloc (bufsize);
-
-  while (fgets (line + length, bufsize - length, fp))
-    {
-      length += strlen (line + length);
-      if (length == 0)
-       /* Possible for example when reading from a binary file where
-          a line begins with \0.  */
-       continue;
-
-      if (line[length - 1] == '\n')
-       break;
-
-      /* fgets() guarantees to read the whole line, or to use up the
-         space we've given it.  We can double the buffer
-         unconditionally.  */
-      bufsize <<= 1;
-      line = xrealloc (line, bufsize);
-    }
-  if (length == 0 || ferror (fp))
-    {
-      xfree (line);
-      return NULL;
-    }
-  if (length + 1 < bufsize)
-    /* Relieve the memory from our exponential greediness.  We say
-       `length + 1' because the terminating \0 is not included in
-       LENGTH.  We don't need to zero-terminate the string ourselves,
-       though, because fgets() does that.  */
-    line = xrealloc (line, length + 1);
-  return line;
-}
-\f
 /* Read FILE into memory.  A pointer to `struct file_memory' are
    returned; use struct element `content' to access file contents, and
    the element `length' to know the file length.  `content' is *not*
    zero-terminated, and you should *not* read or write beyond the [0,
    length) range of characters.
 
-   After you are done with the file contents, call read_file_free to
+   After you are done with the file contents, call wget_read_file_free to
    release the memory.
 
    Depending on the operating system and the type of file that is
-   being read, read_file() either mmap's the file into memory, or
+   being read, wget_read_file() either mmap's the file into memory, or
    reads the file into the core using read().
 
    If file is named "-", fileno(stdin) is used for reading instead.
    If you want to read from a real file named "-", use "./-" instead.  */
 
 struct file_memory *
-read_file (const char *file)
+wget_read_file (const char *file)
 {
   int fd;
   struct file_memory *fm;
@@ -968,7 +1171,7 @@ read_file (const char *file)
        specify PROT_READ and MAP_SHARED for a marginal gain in
        efficiency, but at some cost to generality.  */
     fm->content = mmap (NULL, fm->length, PROT_READ | PROT_WRITE,
-                       MAP_PRIVATE, fd, 0);
+                        MAP_PRIVATE, fd, 0);
     if (fm->content == (char *)MAP_FAILED)
       goto mmap_lose;
     if (!inhibit_close)
@@ -986,41 +1189,41 @@ read_file (const char *file)
 #endif /* HAVE_MMAP */
 
   fm->length = 0;
-  size = 512;                  /* number of bytes fm->contents can
+  size = 512;                   /* number of bytes fm->contents can
                                    hold at any given time. */
   fm->content = xmalloc (size);
   while (1)
     {
       wgint nread;
       if (fm->length > size / 2)
-       {
-         /* #### I'm not sure whether the whole exponential-growth
+        {
+          /* #### I'm not sure whether the whole exponential-growth
              thing makes sense with kernel read.  On Linux at least,
              read() refuses to read more than 4K from a file at a
              single chunk anyway.  But other Unixes might optimize it
              better, and it doesn't *hurt* anything, so I'm leaving
              it.  */
 
-         /* Normally, we grow SIZE exponentially to make the number
+          /* Normally, we grow SIZE exponentially to make the number
              of calls to read() and realloc() logarithmic in relation
              to file size.  However, read() can read an amount of data
              smaller than requested, and it would be unreasonable to
              double SIZE every time *something* was read.  Therefore,
              we double SIZE only when the length exceeds half of the
              entire allocated size.  */
-         size <<= 1;
-         fm->content = xrealloc (fm->content, size);
-       }
+          size <<= 1;
+          fm->content = xrealloc (fm->content, size);
+        }
       nread = read (fd, fm->content + fm->length, size - fm->length);
       if (nread > 0)
-       /* Successful read. */
-       fm->length += nread;
+        /* Successful read. */
+        fm->length += nread;
       else if (nread < 0)
-       /* Error. */
-       goto lose;
+        /* Error. */
+        goto lose;
       else
-       /* EOF */
-       break;
+        /* EOF */
+        break;
     }
   if (!inhibit_close)
     close (fd);
@@ -1045,7 +1248,7 @@ read_file (const char *file)
    memory needed to hold the FM structure itself.  */
 
 void
-read_file_free (struct file_memory *fm)
+wget_read_file_free (struct file_memory *fm)
 {
 #ifdef HAVE_MMAP
   if (fm->mmap_p)
@@ -1069,7 +1272,7 @@ free_vec (char **vec)
     {
       char **p = vec;
       while (*p)
-       xfree (*p++);
+        xfree (*p++);
       xfree (vec);
     }
 }
@@ -1111,12 +1314,12 @@ merge_vecs (char **v1, char **v2)
 char **
 vec_append (char **vec, const char *str)
 {
-  int cnt;                     /* count of vector elements, including
-                                  the one we're about to append */
+  int cnt;                      /* count of vector elements, including
+                                   the one we're about to append */
   if (vec != NULL)
     {
       for (cnt = 0; vec[cnt]; cnt++)
-       ;
+        ;
       ++cnt;
     }
   else
@@ -1215,19 +1418,25 @@ get_grouping_data (const char **sep, const char **grouping)
       struct lconv *lconv = localeconv ();
       cached_sep = lconv->thousands_sep;
       cached_grouping = lconv->grouping;
+#if ! USE_NLS_PROGRESS_BAR
+      /* We can't count column widths, so ensure that the separator
+       * is single-byte only (let check below determine what byte). */
+      if (strlen(cached_sep) > 1)
+        cached_sep = "";
+#endif
       if (!*cached_sep)
-       {
-         /* Many locales (such as "C" or "hr_HR") don't specify
-            grouping, which we still want to use it for legibility.
-            In those locales set the sep char to ',', unless that
-            character is used for decimal point, in which case set it
-            to ".".  */
-         if (*lconv->decimal_point != ',')
-           cached_sep = ",";
-         else
-           cached_sep = ".";
-         cached_grouping = "\x03";
-       }
+        {
+          /* Many locales (such as "C" or "hr_HR") don't specify
+             grouping, which we still want to use it for legibility.
+             In those locales set the sep char to ',', unless that
+             character is used for decimal point, in which case set it
+             to ".".  */
+          if (*lconv->decimal_point != ',')
+            cached_sep = ",";
+          else
+            cached_sep = ".";
+          cached_grouping = "\x03";
+        }
       initialized = true;
     }
   *sep = cached_sep;
@@ -1278,18 +1487,18 @@ with_thousand_seps (wgint n)
       *--p = n % 10 + '0';
       n /= 10;
       if (n == 0)
-       break;
+        break;
       /* Prepend SEP to every groupsize'd digit and get new groupsize.  */
       if (++i == groupsize)
-       {
-         if (seplen == 1)
-           *--p = *sep;
-         else
-           memcpy (p -= seplen, sep, seplen);
-         i = 0;
-         if (*atgroup)
-           groupsize = *atgroup++;
-       }
+        {
+          if (seplen == 1)
+            *--p = *sep;
+          else
+            memcpy (p -= seplen, sep, seplen);
+          i = 0;
+          if (*atgroup)
+            groupsize = *atgroup++;
+        }
     }
   if (negative)
     *--p = '-';
@@ -1314,20 +1523,20 @@ with_thousand_seps (wgint n)
    some detail.  */
 
 char *
-human_readable (HR_NUMTYPE n)
+human_readable (HR_NUMTYPE n, const int acc, const int decimals)
 {
   /* These suffixes are compatible with those of GNU `ls -lh'. */
   static char powers[] =
     {
-      'K',                     /* kilobyte, 2^10 bytes */
-      'M',                     /* megabyte, 2^20 bytes */
-      'G',                     /* gigabyte, 2^30 bytes */
-      'T',                     /* terabyte, 2^40 bytes */
-      'P',                     /* petabyte, 2^50 bytes */
-      'E',                     /* exabyte,  2^60 bytes */
+      'K',                      /* kilobyte, 2^10 bytes */
+      'M',                      /* megabyte, 2^20 bytes */
+      'G',                      /* gigabyte, 2^30 bytes */
+      'T',                      /* terabyte, 2^40 bytes */
+      'P',                      /* petabyte, 2^50 bytes */
+      'E',                      /* exabyte,  2^60 bytes */
     };
   static char buf[8];
-  int i;
+  size_t i;
 
   /* If the quantity is smaller than 1K, just print it. */
   if (n < 1024)
@@ -1342,20 +1551,20 @@ human_readable (HR_NUMTYPE n)
   for (i = 0; i < countof (powers); i++)
     {
       /* At each iteration N is greater than the *subsequent* power.
-        That way N/1024.0 produces a decimal number in the units of
-        *this* power.  */
+         That way N/1024.0 produces a decimal number in the units of
+         *this* power.  */
       if ((n / 1024) < 1024 || i == countof (powers) - 1)
-       {
-         double val = n / 1024.0;
-         /* Print values smaller than 10 with one decimal digits, and
-            others without any decimals.  */
-         snprintf (buf, sizeof (buf), "%.*f%c",
-                   val < 10 ? 1 : 0, val, powers[i]);
-         return buf;
-       }
+        {
+          double val = n / 1024.0;
+          /* Print values smaller than the accuracy level (acc) with (decimal)
+           * decimal digits, and others without any decimals.  */
+          snprintf (buf, sizeof (buf), "%.*f%c",
+                    val < acc ? decimals : 0, val, powers[i]);
+          return buf;
+        }
       n /= 1024;
     }
-  return NULL;                 /* unreached */
+  return NULL;                  /* unreached */
 }
 
 /* Count the digits in the provided number.  Used to allocate space
@@ -1366,7 +1575,7 @@ numdigit (wgint number)
 {
   int cnt = 1;
   if (number < 0)
-    ++cnt;                     /* accomodate '-' */
+    ++cnt;                      /* accomodate '-' */
   while ((number /= 10) != 0)
     ++cnt;
   return cnt;
@@ -1441,8 +1650,8 @@ number_to_string (char *buffer, wgint number)
   if (n < 0)
     {
       if (n < -WGINT_MAX)
-       {
-         /* n = -n would overflow because -n would evaluate to a
+        {
+          /* n = -n would overflow because -n would evaluate to a
              wgint value larger than WGINT_MAX.  Need to make n
              smaller and handle the last digit separately.  */
           int last_digit = n % 10;
@@ -1453,7 +1662,7 @@ number_to_string (char *buffer, wgint number)
             last_digit_char = '0' + last_digit;
           /* After n is made smaller, -n will not overflow. */
           n /= 10;
-       }
+        }
 
       *p++ = '-';
       n = -n;
@@ -1567,6 +1776,17 @@ number_to_static_string (wgint number)
   ringpos = (ringpos + 1) % RING_SIZE;
   return buf;
 }
+
+/* Converts the byte to bits format if --report-bps option is enabled
+ */
+wgint
+convert_to_bits (wgint num)
+{
+  if (opt.report_bps)
+    return num * 8;
+  return num;
+}
+
 \f
 /* Determine the width of the terminal we're running on.  If that's
    not possible, return 0.  */
@@ -1585,7 +1805,7 @@ determine_screen_width (void)
 
   fd = fileno (stderr);
   if (ioctl (fd, TIOCGWINSZ, &wsz) < 0)
-    return 0;                  /* most likely ENOTTY */
+    return 0;                   /* most likely ENOTTY */
 
   return wsz.ws_col;
 #elif defined(WINDOWS)
@@ -1661,9 +1881,9 @@ random_float (void)
   return drand48 ();
 #else  /* not HAVE_DRAND48 */
   return (  random_number (10000) / 10000.0
-         + random_number (10000) / (10000.0 * 10000.0)
-         + random_number (10000) / (10000.0 * 10000.0 * 10000.0)
-         + random_number (10000) / (10000.0 * 10000.0 * 10000.0 * 10000.0));
+          + random_number (10000) / (10000.0 * 10000.0)
+          + random_number (10000) / (10000.0 * 10000.0 * 10000.0)
+          + random_number (10000) / (10000.0 * 10000.0 * 10000.0 * 10000.0));
 #endif /* not HAVE_DRAND48 */
 }
 \f
@@ -1694,9 +1914,10 @@ abort_run_with_timeout (int sig)
   /* We don't have siglongjmp to preserve the set of blocked signals;
      if we longjumped out of the handler at this point, SIGALRM would
      remain blocked.  We must unblock it manually. */
-  int mask = siggetmask ();
-  mask &= ~sigmask (SIGALRM);
-  sigsetmask (mask);
+  sigset_t set;
+  sigemptyset (&set);
+  sigaddset (&set, SIGALRM);
+  sigprocmask (SIG_BLOCK, &set, NULL);
 
   /* Now it's safe to longjump. */
   longjmp (run_with_timeout_env, -1);
@@ -1816,7 +2037,7 @@ run_with_timeout (double timeout, void (*fun) (void *), void *arg)
    define it under Windows, because Windows has its own version of
    run_with_timeout that uses threads.  */
 
-int
+bool
 run_with_timeout (double timeout, void (*fun) (void *), void *arg)
 {
   fun (arg);
@@ -1851,8 +2072,8 @@ xsleep (double seconds)
   if (seconds >= 1)
     {
       /* On some systems, usleep cannot handle values larger than
-        1,000,000.  If the period is larger than that, use sleep
-        first, then add usleep for subsecond accuracy.  */
+         1,000,000.  If the period is larger than that, use sleep
+         first, then add usleep for subsecond accuracy.  */
       sleep (seconds);
       seconds -= (long) seconds;
     }
@@ -1884,8 +2105,8 @@ xsleep (double seconds)
    This implementation does not emit newlines after 76 characters of
    base64 data.  */
 
-int
-base64_encode (const void *data, int length, char *dest)
+size_t
+base64_encode (const void *data, size_t length, char *dest)
 {
   /* Conversion table.  */
   static const char tbl[64] = {
@@ -1934,9 +2155,9 @@ base64_encode (const void *data, int length, char *dest)
 
 /* Store in C the next non-whitespace character from the string, or \0
    when end of string is reached.  */
-#define NEXT_CHAR(c, p) do {                   \
-  c = (unsigned char) *p++;                    \
-} while (ISSPACE (c))
+#define NEXT_CHAR(c, p) do {                    \
+  c = (unsigned char) *p++;                     \
+} while (c_isspace (c))
 
 #define IS_ASCII(c) (((c) & 0x80) == 0)
 
@@ -1952,26 +2173,26 @@ base64_encode (const void *data, int length, char *dest)
 
    This function originates from Free Recode.  */
 
-int
+ssize_t
 base64_decode (const char *base64, void *dest)
 {
   /* Table of base64 values for first 128 characters.  Note that this
      assumes ASCII (but so does Wget in other places).  */
   static const signed char base64_char_to_value[128] =
     {
-      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, /*   0-  9 */
-      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, /*  10- 19 */
-      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, /*  20- 29 */
-      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, /*  30- 39 */
-      -1,  -1,  -1,  62,  -1,  -1,  -1,  63,  52,  53, /*  40- 49 */
-      54,  55,  56,  57,  58,  59,  60,  61,  -1,  -1, /*  50- 59 */
-      -1,  -1,  -1,  -1,  -1,  0,   1,   2,   3,   4,  /*  60- 69 */
-      5,   6,   7,   8,   9,   10,  11,  12,  13,  14, /*  70- 79 */
-      15,  16,  17,  18,  19,  20,  21,  22,  23,  24, /*  80- 89 */
-      25,  -1,  -1,  -1,  -1,  -1,  -1,  26,  27,  28, /*  90- 99 */
-      29,  30,  31,  32,  33,  34,  35,  36,  37,  38, /* 100-109 */
-      39,  40,  41,  42,  43,  44,  45,  46,  47,  48, /* 110-119 */
-      49,  50,  51,  -1,  -1,  -1,  -1,  -1            /* 120-127 */
+      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /*   0-  9 */
+      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /*  10- 19 */
+      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /*  20- 29 */
+      -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  /*  30- 39 */
+      -1,  -1,  -1,  62,  -1,  -1,  -1,  63,  52,  53,  /*  40- 49 */
+      54,  55,  56,  57,  58,  59,  60,  61,  -1,  -1,  /*  50- 59 */
+      -1,  -1,  -1,  -1,  -1,  0,   1,   2,   3,   4,   /*  60- 69 */
+      5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  /*  70- 79 */
+      15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  /*  80- 89 */
+      25,  -1,  -1,  -1,  -1,  -1,  -1,  26,  27,  28,  /*  90- 99 */
+      29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  /* 100-109 */
+      39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  /* 110-119 */
+      49,  50,  51,  -1,  -1,  -1,  -1,  -1             /* 120-127 */
     };
 #define BASE64_CHAR_TO_VALUE(c) ((int) base64_char_to_value[c])
 #define IS_BASE64(c) ((IS_ASCII (c) && BASE64_CHAR_TO_VALUE (c) >= 0) || c == '=')
@@ -1987,36 +2208,36 @@ base64_decode (const char *base64, void *dest)
       /* Process first byte of a quadruplet.  */
       NEXT_CHAR (c, p);
       if (!c)
-       break;
+        break;
       if (c == '=' || !IS_BASE64 (c))
-       return -1;              /* illegal char while decoding base64 */
+        return -1;              /* illegal char while decoding base64 */
       value = BASE64_CHAR_TO_VALUE (c) << 18;
 
       /* Process second byte of a quadruplet.  */
       NEXT_CHAR (c, p);
       if (!c)
-       return -1;              /* premature EOF while decoding base64 */
+        return -1;              /* premature EOF while decoding base64 */
       if (c == '=' || !IS_BASE64 (c))
-       return -1;              /* illegal char while decoding base64 */
+        return -1;              /* illegal char while decoding base64 */
       value |= BASE64_CHAR_TO_VALUE (c) << 12;
       *q++ = value >> 16;
 
       /* Process third byte of a quadruplet.  */
       NEXT_CHAR (c, p);
       if (!c)
-       return -1;              /* premature EOF while decoding base64 */
+        return -1;              /* premature EOF while decoding base64 */
       if (!IS_BASE64 (c))
-       return -1;              /* illegal char while decoding base64 */
+        return -1;              /* illegal char while decoding base64 */
 
       if (c == '=')
-       {
-         NEXT_CHAR (c, p);
-         if (!c)
-           return -1;          /* premature EOF while decoding base64 */
-         if (c != '=')
-           return -1;          /* padding `=' expected but not found */
-         continue;
-       }
+        {
+          NEXT_CHAR (c, p);
+          if (!c)
+            return -1;          /* premature EOF while decoding base64 */
+          if (c != '=')
+            return -1;          /* padding `=' expected but not found */
+          continue;
+        }
 
       value |= BASE64_CHAR_TO_VALUE (c) << 6;
       *q++ = 0xff & value >> 8;
@@ -2024,11 +2245,11 @@ base64_decode (const char *base64, void *dest)
       /* Process fourth byte of a quadruplet.  */
       NEXT_CHAR (c, p);
       if (!c)
-       return -1;              /* premature EOF while decoding base64 */
+        return -1;              /* premature EOF while decoding base64 */
       if (c == '=')
-       continue;
+        continue;
       if (!IS_BASE64 (c))
-       return -1;              /* illegal char while decoding base64 */
+        return -1;              /* illegal char while decoding base64 */
 
       value |= BASE64_CHAR_TO_VALUE (c);
       *q++ = 0xff & value;
@@ -2039,6 +2260,89 @@ base64_decode (const char *base64, void *dest)
   return q - (char *) dest;
 }
 
+#ifdef HAVE_LIBPCRE
+/* Compiles the PCRE regex. */
+void *
+compile_pcre_regex (const char *str)
+{
+  const char *errbuf;
+  int erroffset;
+  pcre *regex = pcre_compile (str, 0, &errbuf, &erroffset, 0);
+  if (! regex)
+    {
+      fprintf (stderr, _("Invalid regular expression %s, %s\n"),
+               quote (str), errbuf);
+      return false;
+    }
+  return regex;
+}
+#endif
+
+/* Compiles the POSIX regex. */
+void *
+compile_posix_regex (const char *str)
+{
+  regex_t *regex = xmalloc (sizeof (regex_t));
+  int errcode = regcomp ((regex_t *) regex, str, REG_EXTENDED | REG_NOSUB);
+  if (errcode != 0)
+    {
+      size_t errbuf_size = regerror (errcode, (regex_t *) regex, NULL, 0);
+      char *errbuf = xmalloc (errbuf_size);
+      regerror (errcode, (regex_t *) regex, errbuf, errbuf_size);
+      fprintf (stderr, _("Invalid regular expression %s, %s\n"),
+               quote (str), errbuf);
+      xfree (errbuf);
+      return NULL;
+    }
+
+  return regex;
+}
+
+#ifdef HAVE_LIBPCRE
+#define OVECCOUNT 30
+/* Matches a PCRE regex.  */
+bool
+match_pcre_regex (const void *regex, const char *str)
+{
+  size_t l = strlen (str);
+  int ovector[OVECCOUNT];
+
+  int rc = pcre_exec ((pcre *) regex, 0, str, (int) l, 0, 0, ovector, OVECCOUNT);
+  if (rc == PCRE_ERROR_NOMATCH)
+    return false;
+  else if (rc < 0)
+    {
+      logprintf (LOG_VERBOSE, _("Error while matching %s: %d\n"),
+                 quote (str), rc);
+      return false;
+    }
+  else
+    return true;
+}
+#undef OVECCOUNT
+#endif
+
+/* Matches a POSIX regex.  */
+bool
+match_posix_regex (const void *regex, const char *str)
+{
+  int rc = regexec ((regex_t *) regex, str, 0, NULL, 0);
+  if (rc == REG_NOMATCH)
+    return false;
+  else if (rc == 0)
+    return true;
+  else
+    {
+      size_t errbuf_size = regerror (rc, opt.acceptregex, NULL, 0);
+      char *errbuf = xmalloc (errbuf_size);
+      regerror (rc, opt.acceptregex, errbuf, errbuf_size);
+      logprintf (LOG_VERBOSE, _("Error while matching %s: %d\n"),
+                 quote (str), rc);
+      xfree (errbuf);
+      return false;
+    }
+}
+
 #undef IS_ASCII
 #undef NEXT_CHAR
 \f
@@ -2047,7 +2351,7 @@ base64_decode (const char *base64, void *dest)
 
 static void
 mergesort_internal (void *base, void *temp, size_t size, size_t from, size_t to,
-                   int (*cmpfun) (const void *, const void *))
+                    int (*cmpfun) (const void *, const void *))
 {
 #define ELT(array, pos) ((char *)(array) + (pos) * size)
   if (from < to)
@@ -2059,16 +2363,16 @@ mergesort_internal (void *base, void *temp, size_t size, size_t from, size_t to,
       i = from;
       j = mid + 1;
       for (k = from; (i <= mid) && (j <= to); k++)
-       if (cmpfun (ELT (base, i), ELT (base, j)) <= 0)
-         memcpy (ELT (temp, k), ELT (base, i++), size);
-       else
-         memcpy (ELT (temp, k), ELT (base, j++), size);
+        if (cmpfun (ELT (base, i), ELT (base, j)) <= 0)
+          memcpy (ELT (temp, k), ELT (base, i++), size);
+        else
+          memcpy (ELT (temp, k), ELT (base, j++), size);
       while (i <= mid)
-       memcpy (ELT (temp, k++), ELT (base, i++), size);
+        memcpy (ELT (temp, k++), ELT (base, i++), size);
       while (j <= to)
-       memcpy (ELT (temp, k++), ELT (base, j++), size);
+        memcpy (ELT (temp, k++), ELT (base, j++), size);
       for (k = from; k <= to; k++)
-       memcpy (ELT (base, k), ELT (temp, k), size);
+        memcpy (ELT (base, k), ELT (temp, k), size);
     }
 #undef ELT
 }
@@ -2079,7 +2383,7 @@ mergesort_internal (void *base, void *temp, size_t size, size_t from, size_t to,
 
 void
 stable_sort (void *base, size_t nmemb, size_t size,
-            int (*cmpfun) (const void *, const void *))
+             int (*cmpfun) (const void *, const void *))
 {
   if (size > 1)
     {
@@ -2123,27 +2427,81 @@ print_decimal (double number)
   return buf;
 }
 
+/* Get the maximum name length for the given path. */
+/* Return 0 if length is unknown. */
+long
+get_max_length (const char *path, int length, int name)
+{
+  long ret;
+  char *p, *d;
+
+  /* Make a copy of the path that we can modify. */
+  p = path ? strdupdelim (path, path + length) : strdup ("");
+
+  for (;;)
+    {
+      errno = 0;
+      /* For an empty path query the current directory. */
+#if HAVE_PATHCONF
+      ret = pathconf (*p ? p : ".", name);
+      if (!(ret < 0 && errno == ENOENT))
+        break;
+#else
+      ret = PATH_MAX;
+#endif
+
+      /* The path does not exist yet, but may be created. */
+      /* Already at current or root directory, give up. */
+      if (!*p || strcmp (p, "/") == 0)
+        break;
+
+      /* Remove one directory level and try again. */
+      d = strrchr (p, '/');
+      if (d == p)
+        p[1] = '\0';  /* check root directory */
+      else if (d)
+        *d = '\0';  /* remove last directory part */
+      else
+        *p = '\0';  /* check current directory */
+    }
+
+  xfree (p);
+
+  if (ret < 0)
+    {
+      /* pathconf() has a message for us. */
+      if (errno != 0)
+          perror ("pathconf");
+
+      /* If (errno == 0) then there is no max length.
+         Even on error return 0 so the caller can continue. */
+      return 0;
+    }
+
+  return ret;
+}
+
 #ifdef TESTING
 
 const char *
-test_subdir_p()
+test_subdir_p(void)
 {
-  int i;
-  struct {
-    char *d1;
-    char *d2;
+  static const struct {
+    const char *d1;
+    const char *d2;
     bool result;
   } test_array[] = {
     { "/somedir", "/somedir", true },
     { "/somedir", "/somedir/d2", true },
     { "/somedir/d1", "/somedir", false },
   };
-  
-  for (i = 0; i < countof(test_array); ++i) 
+  unsigned i;
+
+  for (i = 0; i < countof(test_array); ++i)
     {
       bool res = subdir_p (test_array[i].d1, test_array[i].d2);
 
-      mu_assert ("test_subdir_p: wrong result", 
+      mu_assert ("test_subdir_p: wrong result",
                  res == test_array[i].result);
     }
 
@@ -2151,26 +2509,36 @@ test_subdir_p()
 }
 
 const char *
-test_dir_matches_p()
+test_dir_matches_p(void)
 {
-  int i;
-  struct {
-    char *dirlist[3];
-    char *dir;
+  static struct {
+    const char *dirlist[3];
+    const char *dir;
     bool result;
   } test_array[] = {
     { { "/somedir", "/someotherdir", NULL }, "somedir", true },
     { { "/somedir", "/someotherdir", NULL }, "anotherdir", false },
     { { "/somedir", "/*otherdir", NULL }, "anotherdir", true },
     { { "/somedir/d1", "/someotherdir", NULL }, "somedir/d1", true },
+    { { "*/*d1", "/someotherdir", NULL }, "somedir/d1", true },
     { { "/somedir/d1", "/someotherdir", NULL }, "d1", false },
+    { { "!COMPLETE", NULL, NULL }, "!COMPLETE", true },
+    { { "*COMPLETE", NULL, NULL }, "!COMPLETE", true },
+    { { "*/!COMPLETE", NULL, NULL }, "foo/!COMPLETE", true },
+    { { "*COMPLETE", NULL, NULL }, "foo/!COMPLETE", false },
+    { { "*/*COMPLETE", NULL, NULL }, "foo/!COMPLETE", true },
+    { { "/dir with spaces", NULL, NULL }, "dir with spaces", true },
+    { { "/dir*with*spaces", NULL, NULL }, "dir with spaces", true },
+    { { "/Tmp/has", NULL, NULL }, "/Tmp/has space", false },
+    { { "/Tmp/has", NULL, NULL }, "/Tmp/has,comma", false },
   };
-  
-  for (i = 0; i < countof(test_array); ++i) 
+  unsigned i;
+
+  for (i = 0; i < countof(test_array); ++i)
     {
       bool res = dir_matches_p (test_array[i].dirlist, test_array[i].dir);
-      
-      mu_assert ("test_dir_matches_p: wrong result", 
+
+      mu_assert ("test_dir_matches_p: wrong result",
                  res == test_array[i].result);
     }