+
+/* Encode the octets in DATA of length LENGTH to base64 format,
+ storing the result to DEST. The output will be zero-terminated,
+ and must point to a writable buffer of at least
+ 1+BASE64_LENGTH(length) bytes. The function returns the length of
+ the resulting base64 data, not counting the terminating zero.
+
+ This implementation does not emit newlines after 76 characters of
+ base64 data. */
+
+size_t
+base64_encode (const void *data, size_t length, char *dest)
+{
+ /* Conversion table. */
+ static const char tbl[64] = {
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
+ 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
+ 'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
+ 'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
+ };
+ /* Access bytes in DATA as unsigned char, otherwise the shifts below
+ don't work for data with MSB set. */
+ const unsigned char *s = data;
+ /* Theoretical ANSI violation when length < 3. */
+ const unsigned char *end = (const unsigned char *) data + length - 2;
+ char *p = dest;
+
+ /* Transform the 3x8 bits to 4x6 bits, as required by base64. */
+ for (; s < end; s += 3)
+ {
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
+ *p++ = tbl[((s[1] & 0xf) << 2) + (s[2] >> 6)];
+ *p++ = tbl[s[2] & 0x3f];
+ }
+
+ /* Pad the result if necessary... */
+ switch (length % 3)
+ {
+ case 1:
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[(s[0] & 3) << 4];
+ *p++ = '=';
+ *p++ = '=';
+ break;
+ case 2:
+ *p++ = tbl[s[0] >> 2];
+ *p++ = tbl[((s[0] & 3) << 4) + (s[1] >> 4)];
+ *p++ = tbl[((s[1] & 0xf) << 2)];
+ *p++ = '=';
+ break;
+ }
+ /* ...and zero-terminate it. */
+ *p = '\0';
+
+ return p - 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 (c_isspace (c))
+
+#define IS_ASCII(c) (((c) & 0x80) == 0)
+
+/* Decode data from BASE64 (a null-terminated string) into memory
+ pointed to by DEST. DEST is assumed to be large enough to
+ accomodate the decoded data, which is guaranteed to be no more than
+ 3/4*strlen(base64).
+
+ Since DEST is assumed to contain binary data, it is not
+ NUL-terminated. The function returns the length of the data
+ written to TO. -1 is returned in case of error caused by malformed
+ base64 input.
+
+ This function originates from Free Recode. */
+
+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 */
+ };
+#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 == '=')
+
+ const char *p = base64;
+ char *q = dest;
+
+ while (1)
+ {
+ unsigned char c;
+ unsigned long value;
+
+ /* Process first byte of a quadruplet. */
+ NEXT_CHAR (c, p);
+ if (!c)
+ break;
+ if (c == '=' || !IS_BASE64 (c))
+ 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 */
+ if (c == '=' || !IS_BASE64 (c))
+ 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 */
+ if (!IS_BASE64 (c))
+ 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;
+ }
+
+ value |= BASE64_CHAR_TO_VALUE (c) << 6;
+ *q++ = 0xff & value >> 8;
+
+ /* Process fourth byte of a quadruplet. */
+ NEXT_CHAR (c, p);
+ if (!c)
+ return -1; /* premature EOF while decoding base64 */
+ if (c == '=')
+ continue;
+ if (!IS_BASE64 (c))
+ return -1; /* illegal char while decoding base64 */
+
+ value |= BASE64_CHAR_TO_VALUE (c);
+ *q++ = 0xff & value;
+ }
+#undef IS_BASE64
+#undef BASE64_CHAR_TO_VALUE
+
+ 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
+/* 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);
+ }
+}
+\f
+/* Print a decimal number. If it is equal to or larger than ten, the
+ number is rounded. Otherwise it is printed with one significant
+ digit without trailing zeros and with no more than three fractional
+ digits total. For example, 0.1 is printed as "0.1", 0.035 is
+ printed as "0.04", 0.0091 as "0.009", and 0.0003 as simply "0".
+
+ This is useful for displaying durations because it provides
+ order-of-magnitude information without unnecessary clutter --
+ long-running downloads are shown without the fractional part, and
+ short ones still retain one significant digit. */
+
+const char *
+print_decimal (double number)
+{
+ static char buf[32];
+ double n = number >= 0 ? number : -number;
+
+ if (n >= 9.95)
+ /* Cut off at 9.95 because the below %.1f would round 9.96 to
+ "10.0" instead of "10". OTOH 9.94 will print as "9.9". */
+ snprintf (buf, sizeof buf, "%.0f", number);
+ else if (n >= 0.95)
+ snprintf (buf, sizeof buf, "%.1f", number);
+ else if (n >= 0.001)
+ snprintf (buf, sizeof buf, "%.1g", number);
+ else if (n >= 0.0005)
+ /* round [0.0005, 0.001) to 0.001 */
+ snprintf (buf, sizeof buf, "%.3f", number);
+ else
+ /* print numbers close to 0 as 0, not 0.000 */
+ strcpy (buf, "0");
+
+ 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(void)
+{
+ static const struct {
+ const char *d1;
+ const char *d2;
+ bool result;
+ } test_array[] = {
+ { "/somedir", "/somedir", true },
+ { "/somedir", "/somedir/d2", true },
+ { "/somedir/d1", "/somedir", false },
+ };
+ 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",
+ res == test_array[i].result);
+ }
+
+ return NULL;
+}
+
+const char *
+test_dir_matches_p(void)
+{
+ 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 },
+ };
+ 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",
+ res == test_array[i].result);
+ }
+
+ return NULL;
+}
+
+#endif /* TESTING */
+