transmission 2.83
[tomato.git] / release / src-rt-6.x.4708 / router / transmission / libtransmission / utils.c
blob34c69e2455eabbf279a9717efd302edf33bf6856
1 /*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: utils.c 14241 2014-01-21 03:10:30Z jordan $
8 */
10 #ifdef HAVE_MEMMEM
11 #define _GNU_SOURCE /* glibc's string.h needs this to pick up memmem */
12 #endif
14 #if defined (XCODE_BUILD)
15 #define HAVE_GETPAGESIZE
16 #define HAVE_ICONV_OPEN
17 #define HAVE_MKDTEMP
18 #define HAVE_VALLOC
19 #endif
21 #include <assert.h>
22 #include <ctype.h> /* isdigit (), isalpha (), tolower () */
23 #include <errno.h>
24 #include <float.h> /* DBL_EPSILON */
25 #include <locale.h> /* localeconv () */
26 #include <math.h> /* pow (), fabs (), floor () */
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h> /* strerror (), memset (), memmem () */
31 #include <time.h> /* nanosleep () */
33 #ifdef HAVE_ICONV_OPEN
34 #include <iconv.h>
35 #endif
36 #include <libgen.h> /* basename () */
37 #include <sys/time.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <unistd.h> /* stat (), getcwd (), getpagesize () */
42 #include <event2/buffer.h>
43 #include <event2/event.h>
45 #ifdef WIN32
46 #include <w32api.h>
47 #define WINVER WindowsXP /* freeaddrinfo (), getaddrinfo (), getnameinfo () */
48 #include <direct.h> /* _getcwd () */
49 #include <windows.h> /* Sleep (), GetSystemTimeAsFileTime () */
50 #endif
52 #include "transmission.h"
53 #include "fdlimit.h"
54 #include "ConvertUTF.h"
55 #include "list.h"
56 #include "log.h"
57 #include "utils.h"
58 #include "platform.h" /* tr_lockLock (), TR_PATH_MAX */
59 #include "platform-quota.h" /* tr_device_info_create(), tr_device_info_get_free_space(), tr_device_info_free() */
60 #include "variant.h"
61 #include "version.h"
64 time_t __tr_current_time = 0;
66 /***
67 ****
68 ***/
70 struct tm *
71 tr_localtime_r (const time_t *_clock, struct tm *_result)
73 #ifdef HAVE_LOCALTIME_R
74 return localtime_r (_clock, _result);
75 #else
76 struct tm *p = localtime (_clock);
77 if (p)
78 * (_result) = *p;
79 return p;
80 #endif
83 int
84 tr_gettimeofday (struct timeval * tv)
86 #ifdef _MSC_VER
87 #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
89 FILETIME ft;
90 uint64_t tmp = 0;
92 if (tv == NULL)
94 errno = EINVAL;
95 return -1;
98 GetSystemTimeAsFileTime(&ft);
99 tmp |= ft.dwHighDateTime;
100 tmp <<= 32;
101 tmp |= ft.dwLowDateTime;
102 tmp /= 10; /* to microseconds */
103 tmp -= DELTA_EPOCH_IN_MICROSECS;
105 tv->tv_sec = tmp / 1000000UL;
106 tv->tv_usec = tmp % 1000000UL;
108 return 0;
110 #undef DELTA_EPOCH_IN_MICROSECS
111 #else
113 return gettimeofday (tv, NULL);
115 #endif
118 /***
119 ****
120 ***/
122 void*
123 tr_malloc (size_t size)
125 return size ? malloc (size) : NULL;
128 void*
129 tr_malloc0 (size_t size)
131 return size ? calloc (1, size) : NULL;
134 void
135 tr_free (void * p)
137 if (p != NULL)
138 free (p);
141 void*
142 tr_memdup (const void * src, size_t byteCount)
144 return memcpy (tr_malloc (byteCount), src, byteCount);
147 /***
148 ****
149 ***/
151 const char*
152 tr_strip_positional_args (const char* str)
154 char * out;
155 static size_t bufsize = 0;
156 static char * buf = NULL;
157 const char * in = str;
158 const size_t len = str ? strlen (str) : 0;
160 if (!buf || (bufsize < len))
162 bufsize = len * 2 + 1;
163 buf = tr_renew (char, buf, bufsize);
166 for (out = buf; str && *str; ++str)
168 *out++ = *str;
170 if ((*str == '%') && isdigit (str[1]))
172 const char * tmp = str + 1;
173 while (isdigit (*tmp))
174 ++tmp;
175 if (*tmp == '$')
176 str = tmp[1]=='\'' ? tmp+1 : tmp;
179 if ((*str == '%') && (str[1] == '\''))
180 str = str + 1;
184 *out = '\0';
185 return !in || strcmp (buf, in) ? buf : in;
192 void
193 tr_timerAdd (struct event * timer, int seconds, int microseconds)
195 struct timeval tv;
196 tv.tv_sec = seconds;
197 tv.tv_usec = microseconds;
199 assert (tv.tv_sec >= 0);
200 assert (tv.tv_usec >= 0);
201 assert (tv.tv_usec < 1000000);
203 evtimer_add (timer, &tv);
206 void
207 tr_timerAddMsec (struct event * timer, int msec)
209 const int seconds = msec / 1000;
210 const int usec = (msec%1000) * 1000;
211 tr_timerAdd (timer, seconds, usec);
218 uint8_t *
219 tr_loadFile (const char * path,
220 size_t * size)
222 uint8_t * buf;
223 struct stat sb;
224 int fd;
225 ssize_t n;
226 const char * const err_fmt = _("Couldn't read \"%1$s\": %2$s");
228 /* try to stat the file */
229 errno = 0;
230 if (stat (path, &sb))
232 const int err = errno;
233 tr_logAddDebug (err_fmt, path, tr_strerror (errno));
234 errno = err;
235 return NULL;
238 if ((sb.st_mode & S_IFMT) != S_IFREG)
240 tr_logAddError (err_fmt, path, _("Not a regular file"));
241 errno = EISDIR;
242 return NULL;
245 /* Load the torrent file into our buffer */
246 fd = tr_open_file_for_scanning (path);
247 if (fd < 0)
249 const int err = errno;
250 tr_logAddError (err_fmt, path, tr_strerror (errno));
251 errno = err;
252 return NULL;
254 buf = tr_malloc (sb.st_size + 1);
255 if (!buf)
257 const int err = errno;
258 tr_logAddError (err_fmt, path, _("Memory allocation failed"));
259 tr_close_file (fd);
260 errno = err;
261 return NULL;
263 n = read (fd, buf, (size_t)sb.st_size);
264 if (n == -1)
266 const int err = errno;
267 tr_logAddError (err_fmt, path, tr_strerror (errno));
268 tr_close_file (fd);
269 free (buf);
270 errno = err;
271 return NULL;
274 tr_close_file (fd);
275 buf[ sb.st_size ] = '\0';
276 *size = sb.st_size;
277 return buf;
280 char*
281 tr_basename (const char * path)
283 #ifdef _MSC_VER
285 char fname[_MAX_FNAME], ext[_MAX_EXT];
286 if (_splitpath_s (path, NULL, 0, NULL, 0, fname, sizeof (fname), ext, sizeof (ext)) == 0)
288 const size_t tmpLen = strlen(fname) + strlen(ext) + 2;
289 char * const tmp = tr_malloc (tmpLen);
290 if (tmp != NULL)
292 if (_makepath_s (tmp, tmpLen, NULL, NULL, fname, ext) == 0)
293 return tmp;
295 tr_free (tmp);
299 return tr_strdup (".");
301 #else
303 char * tmp = tr_strdup (path);
304 char * ret = tr_strdup (basename (tmp));
305 tr_free (tmp);
306 return ret;
308 #endif
311 char*
312 tr_dirname (const char * path)
314 #ifdef _MSC_VER
316 char drive[_MAX_DRIVE], dir[_MAX_DIR];
317 if (_splitpath_s (path, drive, sizeof (drive), dir, sizeof (dir), NULL, 0, NULL, 0) == 0)
319 const size_t tmpLen = strlen(drive) + strlen(dir) + 2;
320 char * const tmp = tr_malloc (tmpLen);
321 if (tmp != NULL)
323 if (_makepath_s (tmp, tmpLen, drive, dir, NULL, NULL) == 0)
325 size_t len = strlen(tmp);
326 while (len > 0 && (tmp[len - 1] == '/' || tmp[len - 1] == '\\'))
327 tmp[--len] = '\0';
329 return tmp;
332 tr_free (tmp);
336 return tr_strdup (".");
338 #else
340 char * tmp = tr_strdup (path);
341 char * ret = tr_strdup (dirname (tmp));
342 tr_free (tmp);
343 return ret;
345 #endif
348 char*
349 tr_mkdtemp (char * template)
351 #ifdef HAVE_MKDTEMP
352 return mkdtemp (template);
353 #else
354 if (!mktemp (template) || mkdir (template, 0700))
355 return NULL;
356 return template;
357 #endif
361 * @brief Portability wrapper for mkdir ()
363 * A portability wrapper around mkdir ().
364 * On WIN32, the `permissions' argument is unused.
366 * @return zero on success, or -1 if an error occurred
367 * (in which case errno is set appropriately).
369 static int
370 tr_mkdir (const char * path, int permissions UNUSED)
372 #ifdef WIN32
373 if (path && isalpha (path[0]) && path[1] == ':' && !path[2])
374 return 0;
375 return mkdir (path);
376 #else
377 return mkdir (path, permissions);
378 #endif
382 tr_mkdirp (const char * path_in,
383 int permissions)
385 char * p;
386 char * pp;
387 bool done;
388 int tmperr;
389 int rv;
390 struct stat sb;
391 char * path;
393 /* make a temporary copy of path */
394 path = tr_strdup (path_in);
395 if (path == NULL)
397 errno = ENOMEM;
398 return -1;
401 /* walk past the root */
402 p = path;
403 while (*p == TR_PATH_DELIMITER)
404 ++p;
406 pp = p;
407 done = false;
408 while ((p = strchr (pp, TR_PATH_DELIMITER)) || (p = strchr (pp, '\0')))
410 if (!*p)
411 done = true;
412 else
413 *p = '\0';
415 tmperr = errno;
416 rv = stat (path, &sb);
417 errno = tmperr;
418 if (rv)
420 /* Folder doesn't exist yet */
421 if (tr_mkdir (path, permissions))
423 tmperr = errno;
424 tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), path, tr_strerror (tmperr));
425 tr_free (path);
426 errno = tmperr;
427 return -1;
430 else if ((sb.st_mode & S_IFMT) != S_IFDIR)
432 /* Node exists but isn't a folder */
433 char * buf = tr_strdup_printf (_("File \"%s\" is in the way"), path);
434 tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), path_in, buf);
435 tr_free (buf);
436 tr_free (path);
437 errno = ENOTDIR;
438 return -1;
441 if (done)
442 break;
444 *p = TR_PATH_DELIMITER;
445 p++;
446 pp = p;
449 tr_free (path);
450 return 0;
453 char*
454 tr_buildPath (const char *first_element, ...)
456 const char * element;
457 char * buf;
458 char * pch;
459 va_list vl;
460 size_t bufLen = 0;
462 /* pass 1: allocate enough space for the string */
463 va_start (vl, first_element);
464 element = first_element;
465 while (element)
467 bufLen += strlen (element) + 1;
468 element = va_arg (vl, const char*);
470 pch = buf = tr_new (char, bufLen);
471 va_end (vl);
472 if (buf == NULL)
473 return NULL;
475 /* pass 2: build the string piece by piece */
476 va_start (vl, first_element);
477 element = first_element;
478 while (element)
480 const size_t elementLen = strlen (element);
481 memcpy (pch, element, elementLen);
482 pch += elementLen;
483 *pch++ = TR_PATH_DELIMITER;
484 element = va_arg (vl, const char*);
486 va_end (vl);
488 /* terminate the string. if nonempty, eat the unwanted trailing slash */
489 if (pch != buf)
490 --pch;
491 *pch++ = '\0';
493 /* sanity checks & return */
494 assert (pch - buf == (off_t)bufLen);
495 return buf;
498 #ifdef SYS_DARWIN
499 #define TR_STAT_MTIME(sb)((sb).st_mtimespec.tv_sec)
500 #else
501 #define TR_STAT_MTIME(sb)((sb).st_mtime)
502 #endif
504 bool
505 tr_fileExists (const char * filename, time_t * mtime)
507 struct stat sb;
508 const bool ok = !stat (filename, &sb);
510 if (ok && (mtime != NULL))
511 *mtime = TR_STAT_MTIME (sb);
513 return ok;
516 int64_t
517 tr_getDirFreeSpace (const char * dir)
519 int64_t free_space;
521 if (!dir || !*dir)
523 errno = EINVAL;
524 free_space = -1;
526 else
528 struct tr_device_info * info;
529 info = tr_device_info_create (dir);
530 free_space = tr_device_info_get_free_space (info);
531 tr_device_info_free (info);
534 return free_space;
537 /****
538 *****
539 ****/
541 char*
542 evbuffer_free_to_str (struct evbuffer * buf)
544 const size_t n = evbuffer_get_length (buf);
545 char * ret = tr_new (char, n + 1);
546 evbuffer_copyout (buf, ret, n);
547 evbuffer_free (buf);
548 ret[n] = '\0';
549 return ret;
552 char*
553 tr_strdup (const void * in)
555 return tr_strndup (in, in ? (int)strlen ((const char *)in) : 0);
558 char*
559 tr_strndup (const void * in, int len)
561 char * out = NULL;
563 if (len < 0)
565 out = tr_strdup (in);
567 else if (in)
569 out = tr_malloc (len + 1);
571 if (out != NULL)
573 memcpy (out, in, len);
574 out[len] = '\0';
578 return out;
581 const char*
582 tr_memmem (const char * haystack, size_t haystacklen,
583 const char * needle, size_t needlelen)
585 #ifdef HAVE_MEMMEM
586 return memmem (haystack, haystacklen, needle, needlelen);
587 #else
588 size_t i;
589 if (!needlelen)
590 return haystack;
591 if (needlelen > haystacklen || !haystack || !needle)
592 return NULL;
593 for (i=0; i<=haystacklen-needlelen; ++i)
594 if (!memcmp (haystack+i, needle, needlelen))
595 return haystack+i;
596 return NULL;
597 #endif
600 char*
601 tr_strdup_printf (const char * fmt, ...)
603 va_list ap;
604 char * ret;
605 size_t len;
606 char statbuf[2048];
608 va_start (ap, fmt);
609 len = evutil_vsnprintf (statbuf, sizeof (statbuf), fmt, ap);
610 va_end (ap);
612 if (len < sizeof (statbuf))
614 ret = tr_strndup (statbuf, len);
616 else
618 ret = tr_new (char, len + 1);
619 va_start (ap, fmt);
620 evutil_vsnprintf (ret, len + 1, fmt, ap);
621 va_end (ap);
624 return ret;
627 const char*
628 tr_strerror (int i)
630 const char * ret = strerror (i);
632 if (ret == NULL)
633 ret = "Unknown Error";
635 return ret;
639 tr_strcmp0 (const char * str1, const char * str2)
641 if (str1 && str2) return strcmp (str1, str2);
642 if (str1) return 1;
643 if (str2) return -1;
644 return 0;
647 /****
648 *****
649 ****/
651 /* https://bugs.launchpad.net/percona-patches/+bug/526863/+attachment/1160199/+files/solaris_10_fix.patch */
652 char*
653 tr_strsep (char ** str, const char * delims)
655 #ifdef HAVE_STRSEP
656 return strsep (str, delims);
657 #else
658 char *token;
660 if (*str == NULL) /* no more tokens */
661 return NULL;
663 token = *str;
664 while (**str != '\0')
666 if (strchr (delims, **str) != NULL)
668 **str = '\0';
669 (*str)++;
670 return token;
672 (*str)++;
675 /* there is not another token */
676 *str = NULL;
678 return token;
679 #endif
682 char*
683 tr_strstrip (char * str)
685 if (str != NULL)
687 size_t pos;
688 size_t len = strlen (str);
690 while (len && isspace (str[len - 1]))
691 --len;
693 for (pos = 0; pos < len && isspace (str[pos]);)
694 ++pos;
696 len -= pos;
697 memmove (str, str + pos, len);
698 str[len] = '\0';
701 return str;
704 bool
705 tr_str_has_suffix (const char *str, const char *suffix)
707 size_t str_len;
708 size_t suffix_len;
710 if (!str)
711 return false;
712 if (!suffix)
713 return true;
715 str_len = strlen (str);
716 suffix_len = strlen (suffix);
717 if (str_len < suffix_len)
718 return false;
720 return !evutil_ascii_strncasecmp (str + str_len - suffix_len, suffix, suffix_len);
723 /****
724 *****
725 ****/
727 uint64_t
728 tr_time_msec (void)
730 struct timeval tv;
732 tr_gettimeofday (&tv);
733 return (uint64_t) tv.tv_sec * 1000 + (tv.tv_usec / 1000);
736 void
737 tr_wait_msec (long int msec)
739 #ifdef WIN32
740 Sleep ((DWORD)msec);
741 #else
742 struct timespec ts;
743 ts.tv_sec = msec / 1000;
744 ts.tv_nsec = (msec % 1000) * 1000000;
745 nanosleep (&ts, NULL);
746 #endif
749 /***
750 ****
751 ***/
754 tr_snprintf (char * buf, size_t buflen, const char * fmt, ...)
756 int len;
757 va_list args;
759 va_start (args, fmt);
760 len = evutil_vsnprintf (buf, buflen, fmt, args);
761 va_end (args);
762 return len;
766 * Copy src to string dst of size siz. At most siz-1 characters
767 * will be copied. Always NUL terminates (unless siz == 0).
768 * Returns strlen (src); if retval >= siz, truncation occurred.
770 size_t
771 tr_strlcpy (char * dst, const void * src, size_t siz)
773 #ifdef HAVE_STRLCPY
774 return strlcpy (dst, src, siz);
775 #else
776 char * d = dst;
777 const char *s = src;
778 size_t n = siz;
780 assert (s);
781 assert (d);
783 /* Copy as many bytes as will fit */
784 if (n != 0)
786 while (--n != 0)
788 if ((*d++ = *s++) == '\0')
789 break;
793 /* Not enough room in dst, add NUL and traverse rest of src */
794 if (n == 0)
796 if (siz != 0)
797 *d = '\0'; /* NUL-terminate dst */
798 while (*s++)
802 return s - (char*)src - 1; /* count does not include NUL */
803 #endif
806 /***
807 ****
808 ***/
810 double
811 tr_getRatio (uint64_t numerator, uint64_t denominator)
813 double ratio;
815 if (denominator > 0)
816 ratio = numerator / (double)denominator;
817 else if (numerator > 0)
818 ratio = TR_RATIO_INF;
819 else
820 ratio = TR_RATIO_NA;
822 return ratio;
825 void
826 tr_sha1_to_hex (char * out, const uint8_t * sha1)
828 int i;
829 static const char hex[] = "0123456789abcdef";
831 for (i=0; i<20; ++i)
833 const unsigned int val = *sha1++;
834 *out++ = hex[val >> 4];
835 *out++ = hex[val & 0xf];
838 *out = '\0';
841 void
842 tr_hex_to_sha1 (uint8_t * out, const char * in)
844 int i;
845 static const char hex[] = "0123456789abcdef";
847 for (i=0; i<20; ++i)
849 const int hi = strchr (hex, tolower (*in++)) - hex;
850 const int lo = strchr (hex, tolower (*in++)) - hex;
851 *out++ = (uint8_t)((hi<<4) | lo);
855 /***
856 ****
857 ***/
859 static bool
860 isValidURLChars (const char * url, int url_len)
862 const char * c;
863 const char * end;
864 static const char * rfc2396_valid_chars =
865 "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
866 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
867 "0123456789" /* digit */
868 "-_.!~*'()" /* mark */
869 ";/?:@&=+$," /* reserved */
870 "<>#%<\"" /* delims */
871 "{}|\\^[]`"; /* unwise */
873 if (url == NULL)
874 return false;
876 for (c=url, end=c+url_len; c && *c && c!=end; ++c)
877 if (!strchr (rfc2396_valid_chars, *c))
878 return false;
880 return true;
883 /** @brief return true if the URL is a http or https or UDP one that Transmission understands */
884 bool
885 tr_urlIsValidTracker (const char * url)
887 bool valid;
889 if (url == NULL)
891 valid = false;
893 else
895 const int len = strlen (url);
897 valid = isValidURLChars (url, len)
898 && !tr_urlParse (url, len, NULL, NULL, NULL, NULL)
899 && (!memcmp (url,"http://",7) || !memcmp (url,"https://",8) || !memcmp (url,"udp://",6));
902 return valid;
905 /** @brief return true if the URL is a http or https or ftp or sftp one that Transmission understands */
906 bool
907 tr_urlIsValid (const char * url, int url_len)
909 bool valid;
911 if (url == NULL)
913 valid = false;
915 else
917 if (url_len < 0)
918 url_len = strlen (url);
920 valid = isValidURLChars (url, url_len)
921 && !tr_urlParse (url, url_len, NULL, NULL, NULL, NULL)
922 && (!memcmp (url,"http://",7) || !memcmp (url,"https://",8) || !memcmp (url,"ftp://",6) || !memcmp (url,"sftp://",7));
925 return valid;
928 bool
929 tr_addressIsIP (const char * str)
931 tr_address tmp;
932 return tr_address_from_string (&tmp, str);
936 tr_urlParse (const char * url_in,
937 int len,
938 char ** setme_protocol,
939 char ** setme_host,
940 int * setme_port,
941 char ** setme_path)
943 int err;
944 int port = 0;
945 int n;
946 char * tmp;
947 char * pch;
948 size_t host_len;
949 size_t protocol_len;
950 const char * host = NULL;
951 const char * protocol = NULL;
952 const char * path = NULL;
954 tmp = tr_strndup (url_in, len);
955 if ((pch = strstr (tmp, "://")))
957 *pch = '\0';
958 protocol = tmp;
959 protocol_len = pch - protocol;
960 pch += 3;
961 if ((n = strcspn (pch, ":/")))
963 const int havePort = pch[n] == ':';
964 host = pch;
965 host_len = n;
966 pch += n;
967 if (pch && *pch)
968 *pch++ = '\0';
969 if (havePort)
971 char * end;
972 port = strtol (pch, &end, 10);
973 pch = end;
975 path = pch;
979 err = !host || !path || !protocol;
981 if (!err && !port)
983 if (!strcmp (protocol, "udp")) port = 80;
984 else if (!strcmp (protocol, "ftp")) port = 21;
985 else if (!strcmp (protocol, "sftp")) port = 22;
986 else if (!strcmp (protocol, "http")) port = 80;
987 else if (!strcmp (protocol, "https")) port = 443;
990 if (!err)
992 if (setme_protocol) *setme_protocol = tr_strndup (protocol, protocol_len);
994 if (setme_host){ ((char*)host)[-3] = ':'; *setme_host =
995 tr_strndup (host, host_len); }
997 if (setme_path){ if (!*path) *setme_path = tr_strdup ("/");
998 else if (path[0] == '/') *setme_path = tr_strdup (path);
999 else { ((char*)path)[-1] = '/'; *setme_path = tr_strdup (path - 1); } }
1001 if (setme_port) *setme_port = port;
1005 tr_free (tmp);
1006 return err;
1009 #include <string.h>
1010 #include <openssl/sha.h>
1011 #include <openssl/hmac.h>
1012 #include <openssl/evp.h>
1013 #include <openssl/bio.h>
1014 #include <openssl/buffer.h>
1016 char *
1017 tr_base64_encode (const void * input, int length, int * setme_len)
1019 int retlen = 0;
1020 char * ret = NULL;
1022 if (input != NULL)
1024 BIO * b64;
1025 BIO * bmem;
1026 BUF_MEM * bptr;
1028 if (length < 1)
1029 length = (int)strlen (input);
1031 bmem = BIO_new (BIO_s_mem ());
1032 b64 = BIO_new (BIO_f_base64 ());
1033 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
1034 b64 = BIO_push (b64, bmem);
1035 BIO_write (b64, input, length);
1036 (void) BIO_flush (b64);
1037 BIO_get_mem_ptr (b64, &bptr);
1038 ret = tr_strndup (bptr->data, bptr->length);
1039 retlen = bptr->length;
1040 BIO_free_all (b64);
1043 if (setme_len)
1044 *setme_len = retlen;
1046 return ret;
1049 char *
1050 tr_base64_decode (const void * input,
1051 int length,
1052 int * setme_len)
1054 char * ret;
1055 BIO * b64;
1056 BIO * bmem;
1057 int retlen;
1059 if (length < 1)
1060 length = strlen (input);
1062 ret = tr_new0 (char, length);
1063 b64 = BIO_new (BIO_f_base64 ());
1064 bmem = BIO_new_mem_buf ((unsigned char*)input, length);
1065 bmem = BIO_push (b64, bmem);
1066 retlen = BIO_read (bmem, ret, length);
1067 if (!retlen)
1069 /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
1070 BIO_free_all (bmem);
1071 b64 = BIO_new (BIO_f_base64 ());
1072 BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
1073 bmem = BIO_new_mem_buf ((unsigned char*)input, length);
1074 bmem = BIO_push (b64, bmem);
1075 retlen = BIO_read (bmem, ret, length);
1078 if (setme_len)
1079 *setme_len = retlen;
1081 BIO_free_all (bmem);
1082 return ret;
1085 /***
1086 ****
1087 ***/
1089 void
1090 tr_removeElementFromArray (void * array,
1091 unsigned int index_to_remove,
1092 size_t sizeof_element,
1093 size_t nmemb)
1095 char * a = array;
1097 memmove (a + sizeof_element * index_to_remove,
1098 a + sizeof_element * (index_to_remove + 1),
1099 sizeof_element * (--nmemb - index_to_remove));
1103 tr_lowerBound (const void * key,
1104 const void * base,
1105 size_t nmemb,
1106 size_t size,
1107 int (* compar)(const void* key, const void* arrayMember),
1108 bool * exact_match)
1110 size_t first = 0;
1111 const char * cbase = base;
1112 bool exact = false;
1114 while (nmemb != 0)
1116 const size_t half = nmemb / 2;
1117 const size_t middle = first + half;
1118 const int c = compar (key, cbase + size*middle);
1120 if (c <= 0)
1122 if (c == 0)
1123 exact = true;
1124 nmemb = half;
1126 else
1128 first = middle + 1;
1129 nmemb = nmemb - half - 1;
1133 *exact_match = exact;
1134 return first;
1137 /***
1138 ****
1139 ****
1140 ***/
1142 /* Byte-wise swap two items of size SIZE.
1143 From glibc, written by Douglas C. Schmidt, LGPL 2.1 or higher */
1144 #define SWAP(a, b, size) \
1145 do { \
1146 register size_t __size = (size); \
1147 register char *__a = (a), *__b = (b); \
1148 if (__a != __b) do { \
1149 char __tmp = *__a; \
1150 *__a++ = *__b; \
1151 *__b++ = __tmp; \
1152 } while (--__size > 0); \
1153 } while (0)
1156 static size_t
1157 quickfindPartition (char * base, size_t left, size_t right, size_t size,
1158 int (*compar)(const void *, const void *), size_t pivotIndex)
1160 size_t i;
1161 size_t storeIndex;
1163 /* move pivot to the end */
1164 SWAP (base+(size*pivotIndex), base+(size*right), size);
1166 storeIndex = left;
1167 for (i=left; i<=right-1; ++i)
1169 if (compar (base+(size*i), base+(size*right)) <= 0)
1171 SWAP (base+(size*storeIndex), base+(size*i), size);
1172 ++storeIndex;
1176 /* move pivot to its final place */
1177 SWAP (base+(size*right), base+(size*storeIndex), size);
1179 /* sanity check the partition */
1180 #ifndef NDEBUG
1181 assert (storeIndex >= left);
1182 assert (storeIndex <= right);
1184 for (i=left; i<storeIndex; ++i)
1185 assert (compar (base+(size*i), base+(size*storeIndex)) <= 0);
1186 for (i=storeIndex+1; i<=right; ++i)
1187 assert (compar (base+(size*i), base+(size*storeIndex)) >= 0);
1188 #endif
1190 return storeIndex;
1193 static void
1194 quickfindFirstK (char * base, size_t left, size_t right, size_t size,
1195 int (*compar)(const void *, const void *), size_t k)
1197 if (right > left)
1199 const size_t pivotIndex = left + (right-left)/2u;
1201 const size_t pivotNewIndex = quickfindPartition (base, left, right, size, compar, pivotIndex);
1203 if (pivotNewIndex > left + k) /* new condition */
1204 quickfindFirstK (base, left, pivotNewIndex-1, size, compar, k);
1205 else if (pivotNewIndex < left + k)
1206 quickfindFirstK (base, pivotNewIndex+1, right, size, compar, k+left-pivotNewIndex-1);
1210 #ifndef NDEBUG
1211 static void
1212 checkBestScoresComeFirst (char * base, size_t nmemb, size_t size,
1213 int (*compar)(const void *, const void *), size_t k)
1215 size_t i;
1216 size_t worstFirstPos = 0;
1218 for (i=1; i<k; ++i)
1219 if (compar (base+(size*worstFirstPos), base+(size*i)) < 0)
1220 worstFirstPos = i;
1222 for (i=0; i<k; ++i)
1223 assert (compar (base+(size*i), base+(size*worstFirstPos)) <= 0);
1224 for (i=k; i<nmemb; ++i)
1225 assert (compar (base+(size*i), base+(size*worstFirstPos)) >= 0);
1227 #endif
1229 void
1230 tr_quickfindFirstK (void * base, size_t nmemb, size_t size,
1231 int (*compar)(const void *, const void *), size_t k)
1233 if (k < nmemb)
1235 quickfindFirstK (base, 0, nmemb-1, size, compar, k);
1237 #ifndef NDEBUG
1238 checkBestScoresComeFirst (base, nmemb, size, compar, k);
1239 #endif
1243 /***
1244 ****
1245 ***/
1247 static char*
1248 strip_non_utf8 (const char * in, size_t inlen)
1250 const char * end;
1251 const char zero = '\0';
1252 struct evbuffer * buf = evbuffer_new ();
1254 while (!tr_utf8_validate (in, inlen, &end))
1256 const int good_len = end - in;
1258 evbuffer_add (buf, in, good_len);
1259 inlen -= (good_len + 1);
1260 in += (good_len + 1);
1261 evbuffer_add (buf, "?", 1);
1264 evbuffer_add (buf, in, inlen);
1265 evbuffer_add (buf, &zero, 1);
1266 return evbuffer_free_to_str (buf);
1269 static char*
1270 to_utf8 (const char * in, size_t inlen)
1272 char * ret = NULL;
1274 #ifdef HAVE_ICONV_OPEN
1275 int i;
1276 const char * encodings[] = { "CURRENT", "ISO-8859-15" };
1277 const int encoding_count = sizeof (encodings) / sizeof (encodings[1]);
1278 const size_t buflen = inlen*4 + 10;
1279 char * out = tr_new (char, buflen);
1281 for (i=0; !ret && i<encoding_count; ++i)
1283 char * inbuf = (char*) in;
1284 char * outbuf = out;
1285 size_t inbytesleft = inlen;
1286 size_t outbytesleft = buflen;
1287 const char * test_encoding = encodings[i];
1289 iconv_t cd = iconv_open ("UTF-8", test_encoding);
1290 if (cd != (iconv_t)-1)
1292 if (iconv (cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)-1)
1293 ret = tr_strndup (out, buflen-outbytesleft);
1294 iconv_close (cd);
1298 tr_free (out);
1299 #endif
1301 if (ret == NULL)
1302 ret = strip_non_utf8 (in, inlen);
1304 return ret;
1307 char*
1308 tr_utf8clean (const char * str, int max_len)
1310 char * ret;
1311 const char * end;
1313 if (max_len < 0)
1314 max_len = (int) strlen (str);
1316 if (tr_utf8_validate (str, max_len, &end))
1317 ret = tr_strndup (str, max_len);
1318 else
1319 ret = to_utf8 (str, max_len);
1321 assert (tr_utf8_validate (ret, -1, NULL));
1322 return ret;
1325 /***
1326 ****
1327 ***/
1329 struct number_range
1331 int low;
1332 int high;
1336 * This should be a single number (ex. "6") or a range (ex. "6-9").
1337 * Anything else is an error and will return failure.
1339 static bool
1340 parseNumberSection (const char * str, int len, struct number_range * setme)
1342 long a, b;
1343 bool success;
1344 char * end;
1345 const int error = errno;
1346 char * tmp = tr_strndup (str, len);
1348 errno = 0;
1349 a = b = strtol (tmp, &end, 10);
1350 if (errno || (end == tmp))
1352 success = false;
1354 else if (*end != '-')
1356 success = true;
1358 else
1360 const char * pch = end + 1;
1361 b = strtol (pch, &end, 10);
1362 if (errno || (pch == end))
1363 success = false;
1364 else if (*end) /* trailing data */
1365 success = false;
1366 else
1367 success = true;
1370 tr_free (tmp);
1372 setme->low = MIN (a, b);
1373 setme->high = MAX (a, b);
1375 errno = error;
1376 return success;
1380 compareInt (const void * va, const void * vb)
1382 const int a = * (const int *)va;
1383 const int b = * (const int *)vb;
1384 return a - b;
1388 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1389 * array of setmeCount ints of all the values in the array.
1390 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1391 * It's the caller's responsibility to call tr_free () on the returned array.
1392 * If a fragment of the string can't be parsed, NULL is returned.
1394 int*
1395 tr_parseNumberRange (const char * str_in, int len, int * setmeCount)
1397 int n = 0;
1398 int * uniq = NULL;
1399 char * str = tr_strndup (str_in, len);
1400 const char * walk;
1401 tr_list * ranges = NULL;
1402 bool success = true;
1404 walk = str;
1405 while (walk && *walk && success)
1407 struct number_range range;
1408 const char * pch = strchr (walk, ',');
1409 if (pch)
1411 success = parseNumberSection (walk, pch-walk, &range);
1412 walk = pch + 1;
1414 else
1416 success = parseNumberSection (walk, strlen (walk), &range);
1417 walk += strlen (walk);
1419 if (success)
1420 tr_list_append (&ranges, tr_memdup (&range, sizeof (struct number_range)));
1423 if (!success)
1425 *setmeCount = 0;
1426 uniq = NULL;
1428 else
1430 int i;
1431 int n2;
1432 tr_list * l;
1433 int * sorted = NULL;
1435 /* build a sorted number array */
1436 n = n2 = 0;
1437 for (l=ranges; l!=NULL; l=l->next)
1439 const struct number_range * r = l->data;
1440 n += r->high + 1 - r->low;
1442 sorted = tr_new (int, n);
1443 if (sorted == NULL)
1445 n = 0;
1446 uniq = NULL;
1448 else
1450 for (l=ranges; l!=NULL; l=l->next)
1452 int i;
1453 const struct number_range * r = l->data;
1454 for (i=r->low; i<=r->high; ++i)
1455 sorted[n2++] = i;
1457 qsort (sorted, n, sizeof (int), compareInt);
1458 assert (n == n2);
1460 /* remove duplicates */
1461 uniq = tr_new (int, n);
1462 if (uniq == NULL)
1464 n = 0;
1466 else
1468 for (i=n=0; i<n2; ++i)
1469 if (!n || uniq[n-1] != sorted[i])
1470 uniq[n++] = sorted[i];
1473 tr_free (sorted);
1477 /* cleanup */
1478 tr_list_free (&ranges, tr_free);
1479 tr_free (str);
1481 /* return the result */
1482 *setmeCount = n;
1483 return uniq;
1486 /***
1487 ****
1488 ***/
1490 double
1491 tr_truncd (double x, int precision)
1493 char * pt;
1494 char buf[128];
1495 const int max_precision = (int) log10 (1.0 / DBL_EPSILON) - 1;
1496 tr_snprintf (buf, sizeof (buf), "%.*f", max_precision, x);
1497 if ((pt = strstr (buf, localeconv ()->decimal_point)))
1498 pt[precision ? precision+1 : 0] = '\0';
1499 return atof (buf);
1502 /* return a truncated double as a string */
1503 static char*
1504 tr_strtruncd (char * buf, double x, int precision, size_t buflen)
1506 tr_snprintf (buf, buflen, "%.*f", precision, tr_truncd (x, precision));
1507 return buf;
1510 char*
1511 tr_strpercent (char * buf, double x, size_t buflen)
1513 if (x < 100.0)
1514 tr_strtruncd (buf, x, 1, buflen);
1515 else
1516 tr_strtruncd (buf, x, 0, buflen);
1518 return buf;
1521 char*
1522 tr_strratio (char * buf, size_t buflen, double ratio, const char * infinity)
1524 if ((int)ratio == TR_RATIO_NA)
1525 tr_strlcpy (buf, _("None"), buflen);
1526 else if ((int)ratio == TR_RATIO_INF)
1527 tr_strlcpy (buf, infinity, buflen);
1528 else
1529 tr_strpercent (buf, ratio, buflen);
1531 return buf;
1534 /***
1535 ****
1536 ***/
1539 tr_moveFile (const char * oldpath, const char * newpath, bool * renamed)
1541 int in;
1542 int out;
1543 char * buf;
1544 struct stat st;
1545 off_t bytesLeft;
1546 const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1548 /* make sure the old file exists */
1549 if (stat (oldpath, &st))
1551 const int err = errno;
1552 errno = err;
1553 return -1;
1555 if (!S_ISREG (st.st_mode))
1557 errno = ENOENT;
1558 return -1;
1560 bytesLeft = st.st_size;
1562 /* make sure the target directory exists */
1564 char * newdir = tr_dirname (newpath);
1565 int i = tr_mkdirp (newdir, 0777);
1566 tr_free (newdir);
1567 if (i)
1568 return i;
1571 /* they might be on the same filesystem... */
1573 const int i = tr_rename (oldpath, newpath);
1574 if (renamed != NULL)
1575 *renamed = i == 0;
1576 if (!i)
1577 return 0;
1580 /* copy the file */
1581 in = tr_open_file_for_scanning (oldpath);
1582 out = tr_open_file_for_writing (newpath);
1583 buf = tr_valloc (buflen);
1584 while (bytesLeft > 0)
1586 ssize_t bytesWritten;
1587 const off_t bytesThisPass = MIN (bytesLeft, (off_t)buflen);
1588 const int numRead = read (in, buf, bytesThisPass);
1589 if (numRead < 0)
1590 break;
1591 bytesWritten = write (out, buf, numRead);
1592 if (bytesWritten < 0)
1593 break;
1594 bytesLeft -= bytesWritten;
1597 /* cleanup */
1598 tr_free (buf);
1599 tr_close_file (out);
1600 tr_close_file (in);
1601 if (bytesLeft != 0)
1602 return -1;
1604 tr_remove (oldpath);
1605 return 0;
1609 tr_rename (const char * oldpath, const char * newpath)
1611 /* FIXME: needs win32 utf-16 support */
1613 return rename (oldpath, newpath);
1617 tr_remove (const char * pathname)
1619 /* FIXME: needs win32 utf-16 support */
1621 return remove (pathname);
1624 bool
1625 tr_is_same_file (const char * filename1, const char * filename2)
1627 #ifdef WIN32
1629 bool res;
1630 HANDLE fh1, fh2;
1631 BY_HANDLE_FILE_INFORMATION fi1, fi2;
1632 int n = strlen (filename1) + 1;
1633 int m = strlen (filename2) + 1;
1634 wchar_t f1nameUTF16[n];
1635 wchar_t f2nameUTF16[m];
1637 MultiByteToWideChar (CP_UTF8, 0, filename1, -1, f1nameUTF16, n);
1638 MultiByteToWideChar (CP_UTF8, 0, filename2, -1, f2nameUTF16, m);
1639 fh1 = CreateFileW (chkFilename (f1nameUTF16), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1640 fh2 = CreateFileW (chkFilename (f2nameUTF16), 0, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
1641 res = GetFileInformationByHandle (fh1, &fi1)
1642 && GetFileInformationByHandle (fh2, &fi2)
1643 && (fi1.dwVolumeSerialNumber == fi2.dwVolumeSerialNumber)
1644 && (fi1.nFileIndexHigh == fi2.nFileIndexHigh)
1645 && (fi1.nFileIndexLow == fi2.nFileIndexLow);
1646 CloseHandle (fh1);
1647 CloseHandle (fh2);
1648 return res;
1650 #else
1652 struct stat sb1, sb2;
1654 return !stat (filename1, &sb1)
1655 && !stat (filename2, &sb2)
1656 && (sb1.st_dev == sb2.st_dev)
1657 && (sb1.st_ino == sb2.st_ino);
1659 #endif
1662 /***
1663 ****
1664 ***/
1666 void*
1667 tr_valloc (size_t bufLen)
1669 size_t allocLen;
1670 void * buf = NULL;
1671 static size_t pageSize = 0;
1673 if (!pageSize)
1675 #ifdef HAVE_GETPAGESIZE
1676 pageSize = (size_t) getpagesize ();
1677 #else /* guess */
1678 pageSize = 4096;
1679 #endif
1682 allocLen = pageSize;
1683 while (allocLen < bufLen)
1684 allocLen += pageSize;
1686 #ifdef HAVE_POSIX_MEMALIGN
1687 if (!buf)
1688 if (posix_memalign (&buf, pageSize, allocLen))
1689 buf = NULL; /* just retry with valloc/malloc */
1690 #endif
1691 #ifdef HAVE_VALLOC
1692 if (!buf)
1693 buf = valloc (allocLen);
1694 #endif
1695 if (!buf)
1696 buf = tr_malloc (allocLen);
1698 return buf;
1701 char *
1702 tr_realpath (const char * path, char * resolved_path)
1704 #ifdef WIN32
1705 /* From a message to the Mingw-msys list, Jun 2, 2005 by Mark Junker. */
1706 if (GetFullPathNameA (path, TR_PATH_MAX, resolved_path, NULL) == 0)
1707 return NULL;
1708 return resolved_path;
1709 #else
1710 return realpath (path, resolved_path);
1711 #endif
1714 /***
1715 ****
1716 ***/
1718 uint64_t
1719 tr_htonll (uint64_t x)
1721 #ifdef HAVE_HTONLL
1722 return htonll (x);
1723 #else
1724 /* fallback code by bdonlan at
1725 * http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c/875505#875505 */
1726 union { uint32_t lx[2]; uint64_t llx; } u;
1727 u.lx[0] = htonl (x >> 32);
1728 u.lx[1] = htonl (x & 0xFFFFFFFFULL);
1729 return u.llx;
1730 #endif
1733 uint64_t
1734 tr_ntohll (uint64_t x)
1736 #ifdef HAVE_NTOHLL
1737 return ntohll (x);
1738 #else
1739 /* fallback code by bdonlan at
1740 * http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c/875505#875505 */
1741 union { uint32_t lx[2]; uint64_t llx; } u;
1742 u.llx = x;
1743 return ((uint64_t)ntohl (u.lx[0]) << 32) | (uint64_t)ntohl (u.lx[1]);
1744 #endif
1747 /***
1748 ****
1749 ****
1750 ****
1751 ***/
1753 struct formatter_unit
1755 char * name;
1756 int64_t value;
1759 struct formatter_units
1761 struct formatter_unit units[4];
1764 enum { TR_FMT_KB, TR_FMT_MB, TR_FMT_GB, TR_FMT_TB };
1766 static void
1767 formatter_init (struct formatter_units * units,
1768 unsigned int kilo,
1769 const char * kb, const char * mb,
1770 const char * gb, const char * tb)
1772 uint64_t value;
1774 value = kilo;
1775 units->units[TR_FMT_KB].name = tr_strdup (kb);
1776 units->units[TR_FMT_KB].value = value;
1778 value *= kilo;
1779 units->units[TR_FMT_MB].name = tr_strdup (mb);
1780 units->units[TR_FMT_MB].value = value;
1782 value *= kilo;
1783 units->units[TR_FMT_GB].name = tr_strdup (gb);
1784 units->units[TR_FMT_GB].value = value;
1786 value *= kilo;
1787 units->units[TR_FMT_TB].name = tr_strdup (tb);
1788 units->units[TR_FMT_TB].value = value;
1791 static char*
1792 formatter_get_size_str (const struct formatter_units * u,
1793 char * buf, int64_t bytes, size_t buflen)
1795 int precision;
1796 double value;
1797 const char * units;
1798 const struct formatter_unit * unit;
1800 if (bytes < u->units[1].value) unit = &u->units[0];
1801 else if (bytes < u->units[2].value) unit = &u->units[1];
1802 else if (bytes < u->units[3].value) unit = &u->units[2];
1803 else unit = &u->units[3];
1805 value = (double)bytes / unit->value;
1806 units = unit->name;
1808 if (unit->value == 1)
1809 precision = 0;
1810 else if (value < 100)
1811 precision = 2;
1812 else
1813 precision = 1;
1815 tr_snprintf (buf, buflen, "%.*f %s", precision, value, units);
1816 return buf;
1819 static struct formatter_units size_units;
1821 void
1822 tr_formatter_size_init (unsigned int kilo,
1823 const char * kb, const char * mb,
1824 const char * gb, const char * tb)
1826 formatter_init (&size_units, kilo, kb, mb, gb, tb);
1829 char*
1830 tr_formatter_size_B (char * buf, int64_t bytes, size_t buflen)
1832 return formatter_get_size_str (&size_units, buf, bytes, buflen);
1835 static struct formatter_units speed_units;
1837 unsigned int tr_speed_K = 0u;
1839 void
1840 tr_formatter_speed_init (unsigned int kilo,
1841 const char * kb, const char * mb,
1842 const char * gb, const char * tb)
1844 tr_speed_K = kilo;
1845 formatter_init (&speed_units, kilo, kb, mb, gb, tb);
1848 char*
1849 tr_formatter_speed_KBps (char * buf, double KBps, size_t buflen)
1851 const double K = speed_units.units[TR_FMT_KB].value;
1852 double speed = KBps;
1854 if (speed <= 999.95) /* 0.0 KB to 999.9 KB */
1856 tr_snprintf (buf, buflen, "%d %s", (int)speed, speed_units.units[TR_FMT_KB].name);
1858 else
1860 speed /= K;
1862 if (speed <= 99.995) /* 0.98 MB to 99.99 MB */
1863 tr_snprintf (buf, buflen, "%.2f %s", speed, speed_units.units[TR_FMT_MB].name);
1864 else if (speed <= 999.95) /* 100.0 MB to 999.9 MB */
1865 tr_snprintf (buf, buflen, "%.1f %s", speed, speed_units.units[TR_FMT_MB].name);
1866 else
1867 tr_snprintf (buf, buflen, "%.1f %s", speed/K, speed_units.units[TR_FMT_GB].name);
1870 return buf;
1873 static struct formatter_units mem_units;
1875 unsigned int tr_mem_K = 0u;
1877 void
1878 tr_formatter_mem_init (unsigned int kilo,
1879 const char * kb, const char * mb,
1880 const char * gb, const char * tb)
1882 tr_mem_K = kilo;
1883 formatter_init (&mem_units, kilo, kb, mb, gb, tb);
1886 char*
1887 tr_formatter_mem_B (char * buf, int64_t bytes_per_second, size_t buflen)
1889 return formatter_get_size_str (&mem_units, buf, bytes_per_second, buflen);
1892 void
1893 tr_formatter_get_units (void * vdict)
1895 int i;
1896 tr_variant * l;
1897 tr_variant * dict = vdict;
1899 tr_variantDictReserve (dict, 6);
1901 tr_variantDictAddInt (dict, TR_KEY_memory_bytes, mem_units.units[TR_FMT_KB].value);
1902 l = tr_variantDictAddList (dict, TR_KEY_memory_units, 4);
1903 for (i=0; i<4; i++)
1904 tr_variantListAddStr (l, mem_units.units[i].name);
1906 tr_variantDictAddInt (dict, TR_KEY_size_bytes, size_units.units[TR_FMT_KB].value);
1907 l = tr_variantDictAddList (dict, TR_KEY_size_units, 4);
1908 for (i=0; i<4; i++)
1909 tr_variantListAddStr (l, size_units.units[i].name);
1911 tr_variantDictAddInt (dict, TR_KEY_speed_bytes, speed_units.units[TR_FMT_KB].value);
1912 l = tr_variantDictAddList (dict, TR_KEY_speed_units, 4);
1913 for (i=0; i<4; i++)
1914 tr_variantListAddStr (l, speed_units.units[i].name);