]> sjero.net Git - wget/blobdiff - src/utils.c
[svn] Don't cast return type of malloc/realloc. Assume ANSI C signal handlers.
[wget] / src / utils.c
index 2e45c4d5812edb4966cd83aa9ae98e0c88d6a53c..caf8341240530645baf7e4a0735543d5732c5d42 100644 (file)
@@ -31,12 +31,11 @@ so, delete this exception statement from your version.  */
 
 #include <stdio.h>
 #include <stdlib.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else  /* not HAVE_STRING_H */
-# include <strings.h>
-#endif /* not HAVE_STRING_H */
-#include <sys/types.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
@@ -46,9 +45,6 @@ so, delete this exception statement from your version.  */
 #ifdef HAVE_PWD_H
 # include <pwd.h>
 #endif
-#ifdef HAVE_LIMITS_H
-# include <limits.h>
-#endif
 #ifdef HAVE_UTIME_H
 # include <utime.h>
 #endif
@@ -61,11 +57,7 @@ so, delete this exception statement from your version.  */
 #endif
 #include <fcntl.h>
 #include <assert.h>
-#ifdef WGET_USE_STDARG
-# include <stdarg.h>
-#else
-# include <varargs.h>
-#endif
+#include <stdarg.h>
 
 /* For TIOCGWINSZ and friends: */
 #ifdef HAVE_SYS_IOCTL_H
@@ -76,10 +68,7 @@ so, delete this exception statement from your version.  */
 #endif
 
 /* Needed for run_with_timeout. */
-#undef USE_SIGNAL_TIMEOUT
-#ifdef HAVE_SIGNAL_H
-# include <signal.h>
-#endif
+#include <signal.h>
 #ifdef HAVE_SETJMP_H
 # include <setjmp.h>
 #endif
@@ -91,11 +80,9 @@ so, delete this exception statement from your version.  */
 # endif
 #endif
 
+#undef USE_SIGNAL_TIMEOUT
 #ifdef HAVE_SIGNAL
-# ifdef HAVE_SIGSETJMP
-#  define USE_SIGNAL_TIMEOUT
-# endif
-# ifdef HAVE_SIGBLOCK
+# if defined(HAVE_SIGSETJMP) || defined(HAVE_SIGBLOCK)
 #  define USE_SIGNAL_TIMEOUT
 # endif
 #endif
@@ -104,10 +91,6 @@ so, delete this exception statement from your version.  */
 #include "utils.h"
 #include "hash.h"
 
-#ifndef errno
-extern int errno;
-#endif
-
 /* Utility function: like xstrdup(), but also lowercases S.  */
 
 char *
@@ -126,7 +109,7 @@ xstrdup_lower (const char *s)
 char *
 strdupdelim (const char *beg, const char *end)
 {
-  char *res = (char *)xmalloc (end - beg + 1);
+  char *res = xmalloc (end - beg + 1);
   memcpy (res, beg, end - beg);
   res[end - beg] = '\0';
   return res;
@@ -150,7 +133,7 @@ sepstring (const char *s)
     {
       if (*s == ',')
        {
-         res = (char **)xrealloc (res, (i + 2) * sizeof (char *));
+         res = xrealloc (res, (i + 2) * sizeof (char *));
          res[i] = strdupdelim (p, s);
          res[++i] = NULL;
          ++s;
@@ -162,18 +145,12 @@ sepstring (const char *s)
       else
        ++s;
     }
-  res = (char **)xrealloc (res, (i + 2) * sizeof (char *));
+  res = xrealloc (res, (i + 2) * sizeof (char *));
   res[i] = strdupdelim (p, s);
   res[i + 1] = NULL;
   return res;
 }
 \f
-#ifdef WGET_USE_STDARG
-# define VA_START(args, arg1) va_start (args, arg1)
-#else
-# define VA_START(args, ignored) va_start (args)
-#endif
-
 /* Like sprintf, but allocates a string of sufficient size with malloc
    and returns it.  GNU libc has a similar function named asprintf,
    which requires the pointer to the string to be passed.  */
@@ -196,7 +173,7 @@ aprintf (const char *fmt, ...)
       /* See log_vprintf_internal for explanation why it's OK to rely
         on the return value of vsnprintf.  */
 
-      VA_START (args, fmt);
+      va_start (args, fmt);
       n = vsnprintf (str, size, fmt, args);
       va_end (args);
 
@@ -211,7 +188,6 @@ aprintf (const char *fmt, ...)
        size <<= 1;             /* twice the old size */
       str = xrealloc (str, size);
     }
-  return NULL;                 /* unreached */
 }
 
 /* Concatenate the NULL-terminated list of string arguments into
@@ -231,7 +207,7 @@ concat_strings (const char *str0, ...)
   /* Calculate the length of and allocate the resulting string. */
 
   argcount = 0;
-  VA_START (args, str0);
+  va_start (args, str0);
   for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
     {
       int len = strlen (next_str);
@@ -245,7 +221,7 @@ concat_strings (const char *str0, ...)
   /* Copy the strings into the allocated space. */
 
   argcount = 0;
-  VA_START (args, str0);
+  va_start (args, str0);
   for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
     {
       int len;
@@ -358,19 +334,23 @@ fork_to_background (void)
 }
 #endif /* not WINDOWS */
 \f
-/* "Touch" FILE, i.e. make its atime and mtime equal to the time
-   specified with TM.  */
+/* "Touch" FILE, i.e. make its mtime ("modified time") equal the time
+   specified with TM.  The atime ("access time") is set to the current
+   time.  */
+
 void
 touch (const char *file, time_t tm)
 {
 #ifdef HAVE_STRUCT_UTIMBUF
   struct utimbuf times;
-  times.actime = times.modtime = tm;
 #else
-  time_t times[2];
-  times[0] = times[1] = tm;
+  struct {
+    time_t actime;
+    time_t modtime;
+  } times;
 #endif
-
+  times.modtime = tm;
+  times.actime = time (NULL);
   if (utime (file, &times) == -1)
     logprintf (LOG_NOTQUIET, "utime(%s): %s\n", file, strerror (errno));
 }
@@ -628,7 +608,7 @@ file_merge (const char *base, const char *file)
   if (!cut)
     return xstrdup (file);
 
-  result = (char *)xmalloc (cut - base + 1 + strlen (file) + 1);
+  result = xmalloc (cut - base + 1 + strlen (file) + 1);
   memcpy (result, base, cut - base);
   result[cut - base] = '/';
   strcpy (result + (cut - base) + 1, file);
@@ -636,7 +616,7 @@ file_merge (const char *base, const char *file)
   return result;
 }
 \f
-static int in_acclist PARAMS ((const char *const *, const char *, int));
+static int in_acclist (const char *const *, const char *, int);
 
 /* Determine whether a file is acceptable to be followed, according to
    lists of patterns to accept/reject.  */
@@ -678,19 +658,21 @@ static char *
 proclist (char **strlist, const char *s, enum accd flags)
 {
   char **x;
-
   for (x = strlist; *x; x++)
-    if (has_wildcards_p (*x))
-      {
-       if (fnmatch (*x, s, FNM_PATHNAME) == 0)
-         break;
-      }
-    else
-      {
-       char *p = *x + ((flags & ALLABS) && (**x == '/')); /* Remove '/' */
-       if (frontcmp (p, s))
-         break;
-      }
+    {
+      /* Remove leading '/' if ALLABS */
+      char *p = *x + ((flags & ALLABS) && (**x == '/'));
+      if (has_wildcards_p (p))
+       {
+         if (fnmatch (p, s, FNM_PATHNAME) == 0)
+           break;
+       }
+      else
+       {
+         if (frontcmp (p, s))
+           break;
+       }
+    }
   return *x;
 }
 
@@ -863,7 +845,7 @@ read_whole_line (FILE *fp)
 {
   int length = 0;
   int bufsize = 82;
-  char *line = (char *)xmalloc (bufsize);
+  char *line = xmalloc (bufsize);
 
   while (fgets (line + length, bufsize - length, fp))
     {
@@ -1075,11 +1057,35 @@ merge_vecs (char **v1, char **v2)
   /* Count v2.  */
   for (j = 0; v2[j]; j++);
   /* Reallocate v1.  */
-  v1 = (char **)xrealloc (v1, (i + j + 1) * sizeof (char **));
+  v1 = xrealloc (v1, (i + j + 1) * sizeof (char **));
   memcpy (v1 + i, v2, (j + 1) * sizeof (char *));
   xfree (v2);
   return v1;
 }
+
+/* Append a freshly allocated copy of STR to VEC.  If VEC is NULL, it
+   is allocated as needed.  Return the new value of the vector. */
+
+char **
+vec_append (char **vec, const char *str)
+{
+  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
+    cnt = 1;
+  /* Reallocate the array to fit the new element and the NULL. */
+  vec = xrealloc (vec, (cnt + 1) * sizeof (char *));
+  /* Append a copy of STR to the vector. */
+  vec[cnt - 1] = xstrdup (str);
+  vec[cnt] = NULL;
+  return vec;
+}
 \f
 /* Sometimes it's useful to create "sets" of strings, i.e. special
    hash tables where you want to store strings as keys and merely
@@ -1245,11 +1251,11 @@ with_thousand_seps_large (LARGE_INT l)
    usually improves readability."
 
    This intentionally uses kilobyte (KB), megabyte (MB), etc. in their
-   original computer science meaning of "multiples of 1024".
-   Multiples of 1000 would be useless since Wget already adds thousand
-   separators for legibility.  We don't use the "*bibyte" names
-   invented in 1998, and seldom used in practice.  Wikipedia's entry
-   on kilobyte discusses this in some detail.  */
+   original computer science meaning of "powers of 1024".  Powers of
+   1000 would be useless since Wget already displays sizes with
+   thousand separators.  We don't use the "*bibyte" names invented in
+   1998, and seldom used in practice.  Wikipedia's entry on kilobyte
+   discusses this in some detail.  */
 
 char *
 human_readable (wgint n)
@@ -1427,10 +1433,10 @@ number_to_string (char *buffer, wgint number)
   /* wgint is 32 bits wide: no number has more than 10 digits. */
   else                                   DIGITS_10 (1000000000);
 #else
-  /* wgint is 64 bits wide: handle numbers with more than 9 decimal
-     digits.  Constants are constructed by compile-time multiplication
-     to avoid dealing with different notations for 64-bit constants
-     (nnnL, nnnLL, and nnnI64, depending on the compiler).  */
+  /* wgint is 64 bits wide: handle numbers with 9-19 decimal digits.
+     Constants are constructed by compile-time multiplication to avoid
+     dealing with different notations for 64-bit constants
+     (nL/nLL/nI64, depending on the compiler and architecture).  */
   else if (n < 10*(W)1000000000)         DIGITS_10 (1000000000);
   else if (n < 100*(W)1000000000)        DIGITS_11 (10*(W)1000000000);
   else if (n < 1000*(W)1000000000)       DIGITS_12 (100*(W)1000000000);
@@ -1515,352 +1521,6 @@ number_to_static_string (wgint number)
   return buf;
 }
 \f
-/* Support for timers. */
-
-#undef TIMER_WINDOWS
-#undef TIMER_GETTIMEOFDAY
-#undef TIMER_TIME
-
-/* Depending on the OS and availability of gettimeofday(), one and
-   only one of the above constants will be defined.  Virtually all
-   modern Unix systems will define TIMER_GETTIMEOFDAY; Windows will
-   use TIMER_WINDOWS.  TIMER_TIME is a catch-all method for
-   non-Windows systems without gettimeofday.  */
-
-#ifdef WINDOWS
-# define TIMER_WINDOWS
-#else  /* not WINDOWS */
-# ifdef HAVE_GETTIMEOFDAY
-#  define TIMER_GETTIMEOFDAY
-# else
-#  define TIMER_TIME
-# endif
-#endif /* not WINDOWS */
-
-#ifdef TIMER_GETTIMEOFDAY
-typedef struct timeval wget_sys_time;
-#endif
-
-#ifdef TIMER_TIME
-typedef time_t wget_sys_time;
-#endif
-
-#ifdef TIMER_WINDOWS
-typedef union {
-  DWORD lores;          /* In case GetTickCount is used */
-  LARGE_INTEGER hires;  /* In case high-resolution timer is used */
-} wget_sys_time;
-#endif
-
-struct wget_timer {
-  /* Whether the start time has been initialized. */
-  int initialized;
-
-  /* The starting point in time which, subtracted from the current
-     time, yields elapsed time. */
-  wget_sys_time start;
-
-  /* The most recent elapsed time, calculated by wtimer_update().
-     Measured in milliseconds.  */
-  double elapsed_last;
-
-  /* Approximately, the time elapsed between the true start of the
-     measurement and the time represented by START.  */
-  double elapsed_pre_start;
-};
-
-#ifdef TIMER_WINDOWS
-
-/* Whether high-resolution timers are used.  Set by wtimer_initialize_once
-   the first time wtimer_allocate is called. */
-static int using_hires_timers;
-
-/* Frequency of high-resolution timers -- number of updates per
-   millisecond.  Calculated the first time wtimer_allocate is called
-   provided that high-resolution timers are available. */
-static double hires_millisec_freq;
-
-/* The first time a timer is created, determine whether to use
-   high-resolution timers. */
-
-static void
-wtimer_initialize_once (void)
-{
-  static int init_done;
-  if (!init_done)
-    {
-      LARGE_INTEGER freq;
-      init_done = 1;
-      freq.QuadPart = 0;
-      QueryPerformanceFrequency (&freq);
-      if (freq.QuadPart != 0)
-        {
-          using_hires_timers = 1;
-          hires_millisec_freq = (double) freq.QuadPart / 1000.0;
-        }
-     }
-}
-#endif /* TIMER_WINDOWS */
-
-/* Allocate a timer.  Calling wtimer_read on the timer will return
-   zero.  It is not legal to call wtimer_update with a freshly
-   allocated timer -- use wtimer_reset first.  */
-
-struct wget_timer *
-wtimer_allocate (void)
-{
-  struct wget_timer *wt = xnew (struct wget_timer);
-  xzero (*wt);
-
-#ifdef TIMER_WINDOWS
-  wtimer_initialize_once ();
-#endif
-
-  return wt;
-}
-
-/* Allocate a new timer and reset it.  Return the new timer. */
-
-struct wget_timer *
-wtimer_new (void)
-{
-  struct wget_timer *wt = wtimer_allocate ();
-  wtimer_reset (wt);
-  return wt;
-}
-
-/* Free the resources associated with the timer.  Its further use is
-   prohibited.  */
-
-void
-wtimer_delete (struct wget_timer *wt)
-{
-  xfree (wt);
-}
-
-/* Store system time to WST.  */
-
-static void
-wtimer_sys_set (wget_sys_time *wst)
-{
-#ifdef TIMER_GETTIMEOFDAY
-  gettimeofday (wst, NULL);
-#endif
-
-#ifdef TIMER_TIME
-  time (wst);
-#endif
-
-#ifdef TIMER_WINDOWS
-  if (using_hires_timers)
-    {
-      QueryPerformanceCounter (&wst->hires);
-    }
-  else
-    {
-      /* Where hires counters are not available, use GetTickCount rather
-         GetSystemTime, because it is unaffected by clock skew and simpler
-         to use.  Note that overflows don't affect us because we never use
-         absolute values of the ticker, only the differences.  */
-      wst->lores = GetTickCount ();
-    }
-#endif
-}
-
-/* Reset timer WT.  This establishes the starting point from which
-   wtimer_read() will return the number of elapsed milliseconds.
-   It is allowed to reset a previously used timer.  */
-
-void
-wtimer_reset (struct wget_timer *wt)
-{
-  /* Set the start time to the current time. */
-  wtimer_sys_set (&wt->start);
-  wt->elapsed_last = 0;
-  wt->elapsed_pre_start = 0;
-  wt->initialized = 1;
-}
-
-static double
-wtimer_sys_diff (wget_sys_time *wst1, wget_sys_time *wst2)
-{
-#ifdef TIMER_GETTIMEOFDAY
-  return ((double)(wst1->tv_sec - wst2->tv_sec) * 1000
-         + (double)(wst1->tv_usec - wst2->tv_usec) / 1000);
-#endif
-
-#ifdef TIMER_TIME
-  return 1000 * (*wst1 - *wst2);
-#endif
-
-#ifdef WINDOWS
-  if (using_hires_timers)
-    return (wst1->hires.QuadPart - wst2->hires.QuadPart) / hires_millisec_freq;
-  else
-    return wst1->lores - wst2->lores;
-#endif
-}
-
-/* Update the timer's elapsed interval.  This function causes the
-   timer to call gettimeofday (or time(), etc.) to update its idea of
-   current time.  To get the elapsed interval in milliseconds, use
-   wtimer_read.
-
-   This function handles clock skew, i.e. time that moves backwards is
-   ignored.  */
-
-void
-wtimer_update (struct wget_timer *wt)
-{
-  wget_sys_time now;
-  double elapsed;
-
-  assert (wt->initialized != 0);
-
-  wtimer_sys_set (&now);
-  elapsed = wt->elapsed_pre_start + wtimer_sys_diff (&now, &wt->start);
-
-  /* Ideally we'd just return the difference between NOW and
-     wt->start.  However, the system timer can be set back, and we
-     could return a value smaller than when we were last called, even
-     a negative value.  Both of these would confuse the callers, which
-     expect us to return monotonically nondecreasing values.
-
-     Therefore: if ELAPSED is smaller than its previous known value,
-     we reset wt->start to the current time and effectively start
-     measuring from this point.  But since we don't want the elapsed
-     value to start from zero, we set elapsed_pre_start to the last
-     elapsed time and increment all future calculations by that
-     amount.  */
-
-  if (elapsed < wt->elapsed_last)
-    {
-      wt->start = now;
-      wt->elapsed_pre_start = wt->elapsed_last;
-      elapsed = wt->elapsed_last;
-    }
-
-  wt->elapsed_last = elapsed;
-}
-
-/* Return the elapsed time in milliseconds between the last call to
-   wtimer_reset and the last call to wtimer_update.
-
-   A typical use of the timer interface would be:
-
-       struct wtimer *timer = wtimer_new ();
-       ... do something that takes a while ...
-       wtimer_update ();
-       double msecs = wtimer_read ();  */
-
-double
-wtimer_read (const struct wget_timer *wt)
-{
-  return wt->elapsed_last;
-}
-
-/* Return the assessed granularity of the timer implementation, in
-   milliseconds.  This is used by code that tries to substitute a
-   better value for timers that have returned zero.  */
-
-double
-wtimer_granularity (void)
-{
-#ifdef TIMER_GETTIMEOFDAY
-  /* Granularity of gettimeofday varies wildly between architectures.
-     However, it appears that on modern machines it tends to be better
-     than 1ms.  Assume 100 usecs.  (Perhaps the configure process
-     could actually measure this?)  */
-  return 0.1;
-#endif
-
-#ifdef TIMER_TIME
-  return 1000;
-#endif
-
-#ifdef TIMER_WINDOWS
-  if (using_hires_timers)
-    return 1.0 / hires_millisec_freq;
-  else
-    return 10;  /* according to MSDN */
-#endif
-}
-\f
-/* This should probably be at a better place, but it doesn't really
-   fit into html-parse.c.  */
-
-/* The function returns the pointer to the malloc-ed quoted version of
-   string s.  It will recognize and quote numeric and special graphic
-   entities, as per RFC1866:
-
-   `&' -> `&amp;'
-   `<' -> `&lt;'
-   `>' -> `&gt;'
-   `"' -> `&quot;'
-   SP  -> `&#32;'
-
-   No other entities are recognized or replaced.  */
-char *
-html_quote_string (const char *s)
-{
-  const char *b = s;
-  char *p, *res;
-  int i;
-
-  /* Pass through the string, and count the new size.  */
-  for (i = 0; *s; s++, i++)
-    {
-      if (*s == '&')
-       i += 4;                 /* `amp;' */
-      else if (*s == '<' || *s == '>')
-       i += 3;                 /* `lt;' and `gt;' */
-      else if (*s == '\"')
-       i += 5;                 /* `quot;' */
-      else if (*s == ' ')
-       i += 4;                 /* #32; */
-    }
-  res = (char *)xmalloc (i + 1);
-  s = b;
-  for (p = res; *s; s++)
-    {
-      switch (*s)
-       {
-       case '&':
-         *p++ = '&';
-         *p++ = 'a';
-         *p++ = 'm';
-         *p++ = 'p';
-         *p++ = ';';
-         break;
-       case '<': case '>':
-         *p++ = '&';
-         *p++ = (*s == '<' ? 'l' : 'g');
-         *p++ = 't';
-         *p++ = ';';
-         break;
-       case '\"':
-         *p++ = '&';
-         *p++ = 'q';
-         *p++ = 'u';
-         *p++ = 'o';
-         *p++ = 't';
-         *p++ = ';';
-         break;
-       case ' ':
-         *p++ = '&';
-         *p++ = '#';
-         *p++ = '3';
-         *p++ = '2';
-         *p++ = ';';
-         break;
-       default:
-         *p++ = *s;
-       }
-    }
-  *p = '\0';
-  return res;
-}
-
 /* Determine the width of the terminal we're running on.  If that's
    not possible, return 0.  */
 
@@ -1965,7 +1625,7 @@ random_float (void)
 
 static sigjmp_buf run_with_timeout_env;
 
-static RETSIGTYPE
+static void
 abort_run_with_timeout (int sig)
 {
   assert (sig == SIGALRM);
@@ -1976,7 +1636,7 @@ abort_run_with_timeout (int sig)
 
 static jmp_buf run_with_timeout_env;
 
-static RETSIGTYPE
+static void
 abort_run_with_timeout (int sig)
 {
   assert (sig == SIGALRM);
@@ -2171,12 +1831,17 @@ xsleep (double seconds)
 
 #endif /* not WINDOWS */
 
-/* Encode the string S of length LENGTH to base64 format and place it
-   to STORE.  STORE will be 0-terminated, and must point to a writable
-   buffer of at least 1+BASE64_LENGTH(length) bytes.  */
+/* Encode the string STR of length LENGTH to base64 format and place it
+   to B64STORE.  The output will be \0-terminated, and must point to a
+   writable buffer of at least 1+BASE64_LENGTH(length) bytes.  It
+   returns the length of the resulting base64 data, not counting the
+   terminating zero.
 
-void
-base64_encode (const char *s, char *store, int length)
+   This implementation will not emit newlines after 76 characters of
+   base64 data.  */
+
+int
+base64_encode (const char *str, int length, char *b64store)
 {
   /* Conversion table.  */
   static char tbl[64] = {
@@ -2190,7 +1855,8 @@ base64_encode (const char *s, char *store, int length)
     '4','5','6','7','8','9','+','/'
   };
   int i;
-  unsigned char *p = (unsigned char *)store;
+  const unsigned char *s = (const unsigned char *) str;
+  char *p = b64store;
 
   /* Transform the 3x8 bits to 4x6 bits, as required by base64.  */
   for (i = 0; i < length; i += 3)
@@ -2201,13 +1867,17 @@ base64_encode (const char *s, char *store, int length)
       *p++ = tbl[s[2] & 0x3f];
       s += 3;
     }
+
   /* Pad the result if necessary...  */
   if (i == length + 1)
     *(p - 1) = '=';
   else if (i == length + 2)
     *(p - 1) = *(p - 2) = '=';
+
   /* ...and zero-terminate it.  */
   *p = '\0';
+
+  return p - b64store;
 }
 
 #define IS_ASCII(c) (((c) & 0x80) == 0)
@@ -2232,7 +1902,8 @@ base64_encode (const char *s, char *store, int length)
 int
 base64_decode (const char *base64, char *to)
 {
-  /* Table of base64 values for first 128 characters.  */
+  /* Table of base64 values for first 128 characters.  Note that this
+     assumes ASCII (but so does Wget in other places).  */
   static short base64_char_to_value[128] =
     {
       -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, /*   0-  9 */
@@ -2284,7 +1955,7 @@ base64_decode (const char *base64, char *to)
        {
          NEXT_BASE64_CHAR (c, p);
          if (!c)
-           return -1;          /* premature EOF while dcoding base64 */
+           return -1;          /* premature EOF while decoding base64 */
          if (c != '=')
            return -1;          /* padding `=' expected but not found */
          continue;
@@ -2296,7 +1967,7 @@ base64_decode (const char *base64, char *to)
       /* Process fourth byte of a quadruplet.  */
       NEXT_BASE64_CHAR (c, p);
       if (!c)
-       return -1;              /* premature EOF while dcoding base64 */
+       return -1;              /* premature EOF while decoding base64 */
       if (c == '=')
        continue;
 
@@ -2310,3 +1981,49 @@ base64_decode (const char *base64, char *to)
 #undef IS_ASCII
 #undef IS_BASE64
 #undef NEXT_BASE64_CHAR
+\f
+/* Simple merge sort for use by stable_sort.  Implementation courtesy
+   Zeljko Vrba with additional debugging by Nenad Barbutov.  */
+
+static void
+mergesort_internal (void *base, void *temp, size_t size, size_t from, size_t to,
+                   int (*cmpfun) (const void *, const void *))
+{
+#define ELT(array, pos) ((char *)(array) + (pos) * size)
+  if (from < to)
+    {
+      size_t i, j, k;
+      size_t mid = (to + from) / 2;
+      mergesort_internal (base, temp, size, from, mid, cmpfun);
+      mergesort_internal (base, temp, size, mid + 1, to, cmpfun);
+      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);
+      while (i <= mid)
+       memcpy (ELT (temp, k++), ELT (base, i++), size);
+      while (j <= to)
+       memcpy (ELT (temp, k++), ELT (base, j++), size);
+      for (k = from; k <= to; k++)
+       memcpy (ELT (base, k), ELT (temp, k), size);
+    }
+#undef ELT
+}
+
+/* Stable sort with interface exactly like standard library's qsort.
+   Uses mergesort internally, allocating temporary storage with
+   alloca.  */
+
+void
+stable_sort (void *base, size_t nmemb, size_t size,
+            int (*cmpfun) (const void *, const void *))
+{
+  if (size > 1)
+    {
+      void *temp = alloca (nmemb * size * sizeof (void *));
+      mergesort_internal (base, temp, size, 0, nmemb - 1, cmpfun);
+    }
+}