source: trunk/libtransmission/utils.c @ 14327

Last change on this file since 14327 was 14327, checked in by jordan, 8 years ago

(trunk, libt) #4160 - the slow slog to catch trunk up to mike.dld's 4160 diff continues. This step applies 4160-03b-file.patch, which replaces native file operations with the tr_sys_file_*() portability wrappers added in r14321.

  • Property svn:keywords set to Date Rev Author Id
File size: 38.7 KB
Line 
1/*
2 * This file Copyright (C) 2009-2014 Mnemosyne LLC
3 *
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
6 *
7 * $Id: utils.c 14327 2014-07-28 04:13:38Z jordan $
8 */
9
10#ifdef HAVE_MEMMEM
11 #define _GNU_SOURCE /* glibc's string.h needs this to pick up memmem */
12#endif
13
14#if defined (XCODE_BUILD)
15 #define HAVE_GETPAGESIZE
16 #define HAVE_ICONV_OPEN
17 #define HAVE_MKDTEMP
18 #define HAVE_VALLOC
19#endif
20
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 <stdio.h>
28#include <stdlib.h>
29#include <string.h> /* strerror (), memset (), memmem () */
30#include <time.h> /* nanosleep () */
31#include <sys/types.h>
32#include <sys/stat.h>
33
34#ifdef HAVE_ICONV_OPEN
35 #include <iconv.h>
36#endif
37#include <sys/time.h>
38#include <unistd.h> /* getpagesize () */
39
40#include <event2/buffer.h>
41#include <event2/event.h>
42
43#ifdef _WIN32
44 #include <w32api.h>
45 #define WINVER WindowsXP /* freeaddrinfo (), getaddrinfo (), getnameinfo () */
46 #include <windows.h> /* Sleep (), GetSystemTimeAsFileTime () */
47#endif
48
49#include "transmission.h"
50#include "error.h"
51#include "file.h"
52#include "ConvertUTF.h"
53#include "list.h"
54#include "log.h"
55#include "net.h"
56#include "utils.h"
57#include "platform.h" /* tr_lockLock () */
58#include "platform-quota.h" /* tr_device_info_create(), tr_device_info_get_free_space(), tr_device_info_free() */
59#include "variant.h"
60#include "version.h"
61
62
63time_t __tr_current_time   = 0;
64
65/***
66****
67***/
68
69struct tm *
70tr_localtime_r (const time_t *_clock, struct tm *_result)
71{
72#ifdef HAVE_LOCALTIME_R
73  return localtime_r (_clock, _result);
74#else
75  struct tm *p = localtime (_clock);
76  if (p)
77    * (_result) = *p;
78  return p;
79#endif
80}
81
82int
83tr_gettimeofday (struct timeval * tv)
84{
85#ifdef _MSC_VER
86#define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
87
88  FILETIME ft;
89  uint64_t tmp = 0;
90
91  if (tv == NULL)
92    {
93      errno = EINVAL;
94      return -1;
95    }
96
97  GetSystemTimeAsFileTime(&ft);
98  tmp |= ft.dwHighDateTime;
99  tmp <<= 32;
100  tmp |= ft.dwLowDateTime;
101  tmp /= 10; /* to microseconds */
102  tmp -= DELTA_EPOCH_IN_MICROSECS;
103
104  tv->tv_sec = tmp / 1000000UL;
105  tv->tv_usec = tmp % 1000000UL;
106
107  return 0;
108
109#undef DELTA_EPOCH_IN_MICROSECS
110#else
111
112  return gettimeofday (tv, NULL);
113
114#endif
115}
116
117/***
118****
119***/
120
121void*
122tr_malloc (size_t size)
123{
124  return size ? malloc (size) : NULL;
125}
126
127void*
128tr_malloc0 (size_t size)
129{
130  return size ? calloc (1, size) : NULL;
131}
132
133void
134tr_free (void * p)
135{
136  if (p != NULL)
137    free (p);
138}
139
140void*
141tr_memdup (const void * src, size_t byteCount)
142{
143  return memcpy (tr_malloc (byteCount), src, byteCount);
144}
145
146/***
147****
148***/
149
150const char*
151tr_strip_positional_args (const char* str)
152{
153  char * out;
154  static size_t bufsize = 0;
155  static char * buf = NULL;
156  const char * in = str;
157  const size_t  len = str ? strlen (str) : 0;
158
159  if (!buf || (bufsize < len))
160    {
161      bufsize = len * 2 + 1;
162      buf = tr_renew (char, buf, bufsize);
163    }
164
165  for (out = buf; str && *str; ++str)
166    {
167      *out++ = *str;
168
169      if ((*str == '%') && isdigit (str[1]))
170        {
171          const char * tmp = str + 1;
172          while (isdigit (*tmp))
173            ++tmp;
174          if (*tmp == '$')
175            str = tmp[1]=='\'' ? tmp+1 : tmp;
176        }
177
178      if ((*str == '%') && (str[1] == '\''))
179        str = str + 1;
180
181    }
182
183  *out = '\0';
184  return !in || strcmp (buf, in) ? buf : in;
185}
186
187/**
188***
189**/
190
191void
192tr_timerAdd (struct event * timer, int seconds, int microseconds)
193{
194  struct timeval tv;
195  tv.tv_sec = seconds;
196  tv.tv_usec = microseconds;
197
198  assert (tv.tv_sec >= 0);
199  assert (tv.tv_usec >= 0);
200  assert (tv.tv_usec < 1000000);
201
202  evtimer_add (timer, &tv);
203}
204
205void
206tr_timerAddMsec (struct event * timer, int msec)
207{
208  const int seconds =  msec / 1000;
209  const int usec = (msec%1000) * 1000;
210  tr_timerAdd (timer, seconds, usec);
211}
212
213/**
214***
215**/
216
217uint8_t *
218tr_loadFile (const char * path,
219             size_t     * size)
220{
221  uint8_t * buf;
222  tr_sys_path_info info;
223  tr_sys_file_t fd;
224  tr_error * error = NULL;
225  const char * const err_fmt = _("Couldn't read \"%1$s\": %2$s");
226
227  /* try to stat the file */
228  if (!tr_sys_path_get_info (path, 0, &info, &error))
229    {
230      const int err = error->code;
231      tr_logAddDebug (err_fmt, path, error->message);
232      tr_error_free (error);
233      errno = err;
234      return NULL;
235    }
236
237  if (info.type != TR_SYS_PATH_IS_FILE)
238    {
239      tr_logAddError (err_fmt, path, _("Not a regular file"));
240      errno = EISDIR;
241      return NULL;
242    }
243
244  /* file size should be able to fit into size_t */
245  if (sizeof(info.size) > sizeof(*size))
246    assert (info.size <= SIZE_MAX);
247
248  /* Load the torrent file into our buffer */
249  fd = tr_sys_file_open (path, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, &error);
250  if (fd == TR_BAD_SYS_FILE)
251    {
252      const int err = error->code;
253      tr_logAddError (err_fmt, path, error->message);
254      tr_error_free (error);
255      errno = err;
256      return NULL;
257    }
258  buf = tr_malloc (info.size + 1);
259  if (!buf)
260    {
261      const int err = errno;
262      tr_logAddError (err_fmt, path, _("Memory allocation failed"));
263      tr_sys_file_close (fd, NULL);
264      errno = err;
265      return NULL;
266    }
267  if (!tr_sys_file_read (fd, buf, info.size, NULL, &error))
268    {
269      const int err = error->code;
270      tr_logAddError (err_fmt, path, error->message);
271      tr_sys_file_close (fd, NULL);
272      free (buf);
273      tr_error_free (error);
274      errno = err;
275      return NULL;
276    }
277
278  tr_sys_file_close (fd, NULL);
279  buf[info.size] = '\0';
280  *size = info.size;
281  return buf;
282}
283
284char*
285tr_mkdtemp (char * template)
286{
287#ifdef HAVE_MKDTEMP
288  return mkdtemp (template);
289#else
290  if (!mktemp (template) || mkdir (template, 0700))
291    return NULL;
292  return template;
293#endif
294}
295
296/**
297 * @brief Portability wrapper for mkdir ()
298 *
299 * A portability wrapper around mkdir ().
300 * On WIN32, the `permissions' argument is unused.
301 *
302 * @return zero on success, or -1 if an error occurred
303 * (in which case errno is set appropriately).
304 */
305static int
306tr_mkdir (const char * path, int permissions UNUSED)
307{
308#ifdef _WIN32
309  if (path && isalpha (path[0]) && path[1] == ':' && !path[2])
310    return 0;
311  return mkdir (path);
312#else
313  return mkdir (path, permissions);
314#endif
315}
316
317int
318tr_mkdirp (const char * path_in,
319           int          permissions)
320{
321  char * p;
322  char * pp;
323  bool done;
324  int tmperr;
325  int rv;
326  struct stat sb;
327  char * path;
328
329  /* make a temporary copy of path */
330  path = tr_strdup (path_in);
331  if (path == NULL)
332    {
333      errno = ENOMEM;
334      return -1;
335    }
336
337  /* walk past the root */
338  p = path;
339  while (*p == TR_PATH_DELIMITER)
340    ++p;
341
342  pp = p;
343  done = false;
344  while ((p = strchr (pp, TR_PATH_DELIMITER)) || (p = strchr (pp, '\0')))
345    {
346      if (!*p)
347        done = true;
348      else
349        *p = '\0';
350
351      tmperr = errno;
352      rv = stat (path, &sb);
353      errno = tmperr;
354      if (rv)
355        {
356          /* Folder doesn't exist yet */
357          if (tr_mkdir (path, permissions))
358            {
359              tmperr = errno;
360              tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), path, tr_strerror (tmperr));
361              tr_free (path);
362              errno = tmperr;
363              return -1;
364            }
365        }
366      else if ((sb.st_mode & S_IFMT) != S_IFDIR)
367        {
368          /* Node exists but isn't a folder */
369          char * buf = tr_strdup_printf (_("File \"%s\" is in the way"), path);
370          tr_logAddError (_("Couldn't create \"%1$s\": %2$s"), path_in, buf);
371          tr_free (buf);
372          tr_free (path);
373          errno = ENOTDIR;
374          return -1;
375        }
376
377      if (done)
378        break;
379
380      *p = TR_PATH_DELIMITER;
381      p++;
382      pp = p;
383    }
384
385  tr_free (path);
386  return 0;
387}
388
389char*
390tr_buildPath (const char *first_element, ...)
391{
392  const char * element;
393  char * buf;
394  char * pch;
395  va_list vl;
396  size_t bufLen = 0;
397
398  /* pass 1: allocate enough space for the string */
399  va_start (vl, first_element);
400  element = first_element;
401  while (element)
402    {
403      bufLen += strlen (element) + 1;
404      element = va_arg (vl, const char*);
405    }
406  pch = buf = tr_new (char, bufLen);
407  va_end (vl);
408  if (buf == NULL)
409    return NULL;
410
411  /* pass 2: build the string piece by piece */
412  va_start (vl, first_element);
413  element = first_element;
414  while (element)
415    {
416      const size_t elementLen = strlen (element);
417      memcpy (pch, element, elementLen);
418      pch += elementLen;
419      *pch++ = TR_PATH_DELIMITER;
420      element = va_arg (vl, const char*);
421    }
422  va_end (vl);
423
424  /* terminate the string. if nonempty, eat the unwanted trailing slash */
425  if (pch != buf)
426    --pch;
427  *pch++ = '\0';
428
429  /* sanity checks & return */
430  assert (pch - buf == (off_t)bufLen);
431  return buf;
432}
433
434int64_t
435tr_getDirFreeSpace (const char * dir)
436{
437  int64_t free_space;
438
439  if (!dir || !*dir)
440    {
441      errno = EINVAL;
442      free_space = -1;
443    }
444  else
445    {
446      struct tr_device_info * info;
447      info = tr_device_info_create (dir);
448      free_space = tr_device_info_get_free_space (info);
449      tr_device_info_free (info);
450    }
451
452  return free_space;
453}
454
455/****
456*****
457****/
458
459char*
460evbuffer_free_to_str (struct evbuffer * buf)
461{
462  const size_t n = evbuffer_get_length (buf);
463  char * ret = tr_new (char, n + 1);
464  evbuffer_copyout (buf, ret, n);
465  evbuffer_free (buf);
466  ret[n] = '\0';
467  return ret;
468}
469
470char*
471tr_strdup (const void * in)
472{
473  return tr_strndup (in, in ? (int)strlen ((const char *)in) : 0);
474}
475
476char*
477tr_strndup (const void * in, int len)
478{
479  char * out = NULL;
480
481  if (len < 0)
482    {
483      out = tr_strdup (in);
484    }
485  else if (in)
486    {
487      out = tr_malloc (len + 1);
488
489      if (out != NULL)
490        {
491          memcpy (out, in, len);
492          out[len] = '\0';
493        }
494    }
495
496  return out;
497}
498
499const char*
500tr_memmem (const char * haystack, size_t haystacklen,
501           const char * needle, size_t needlelen)
502{
503#ifdef HAVE_MEMMEM
504  return memmem (haystack, haystacklen, needle, needlelen);
505#else
506  size_t i;
507  if (!needlelen)
508    return haystack;
509  if (needlelen > haystacklen || !haystack || !needle)
510    return NULL;
511  for (i=0; i<=haystacklen-needlelen; ++i)
512    if (!memcmp (haystack+i, needle, needlelen))
513      return haystack+i;
514  return NULL;
515#endif
516}
517
518char*
519tr_strdup_printf (const char * fmt, ...)
520{
521  va_list ap;
522  char * ret;
523
524  va_start (ap, fmt);
525  ret = tr_strdup_vprintf (fmt, ap);
526  va_end (ap);
527
528  return ret;
529}
530
531char *
532tr_strdup_vprintf (const char * fmt,
533                   va_list      args)
534{
535  struct evbuffer * buf = evbuffer_new ();
536  evbuffer_add_vprintf (buf, fmt, args);
537  return evbuffer_free_to_str (buf);
538}
539
540const char*
541tr_strerror (int i)
542{
543  const char * ret = strerror (i);
544
545  if (ret == NULL)
546    ret = "Unknown Error";
547
548  return ret;
549}
550
551int
552tr_strcmp0 (const char * str1, const char * str2)
553{
554  if (str1 && str2) return strcmp (str1, str2);
555  if (str1) return 1;
556  if (str2) return -1;
557  return 0;
558}
559
560/****
561*****
562****/
563
564/* https://bugs.launchpad.net/percona-patches/+bug/526863/+attachment/1160199/+files/solaris_10_fix.patch */
565char*
566tr_strsep (char ** str, const char * delims)
567{
568#ifdef HAVE_STRSEP
569  return strsep (str, delims);
570#else
571  char *token;
572
573  if (*str == NULL) /* no more tokens */
574    return NULL;
575
576  token = *str;
577  while (**str != '\0')
578    {
579      if (strchr (delims, **str) != NULL)
580        {
581          **str = '\0';
582          (*str)++;
583            return token;
584        }
585      (*str)++;
586    }
587
588  /* there is not another token */
589  *str = NULL;
590
591  return token;
592#endif
593}
594
595char*
596tr_strstrip (char * str)
597{
598  if (str != NULL)
599    {
600      size_t pos;
601      size_t len = strlen (str);
602
603      while (len && isspace (str[len - 1]))
604        --len;
605
606      for (pos = 0; pos < len && isspace (str[pos]);)
607        ++pos;
608
609      len -= pos;
610      memmove (str, str + pos, len);
611      str[len] = '\0';
612    }
613
614  return str;
615}
616
617bool
618tr_str_has_suffix (const char *str, const char *suffix)
619{
620  size_t str_len;
621  size_t suffix_len;
622
623  if (!str)
624    return false;
625  if (!suffix)
626    return true;
627
628  str_len = strlen (str);
629  suffix_len = strlen (suffix);
630  if (str_len < suffix_len)
631    return false;
632
633  return !evutil_ascii_strncasecmp (str + str_len - suffix_len, suffix, suffix_len);
634}
635
636/****
637*****
638****/
639
640uint64_t
641tr_time_msec (void)
642{
643  struct timeval tv;
644
645  tr_gettimeofday (&tv);
646  return (uint64_t) tv.tv_sec * 1000 + (tv.tv_usec / 1000);
647}
648
649void
650tr_wait_msec (long int msec)
651{
652#ifdef _WIN32
653  Sleep ((DWORD)msec);
654#else
655  struct timespec ts;
656  ts.tv_sec = msec / 1000;
657  ts.tv_nsec = (msec % 1000) * 1000000;
658  nanosleep (&ts, NULL);
659#endif
660}
661
662/***
663****
664***/
665
666int
667tr_snprintf (char * buf, size_t buflen, const char * fmt, ...)
668{
669  int len;
670  va_list args;
671
672  va_start (args, fmt);
673  len = evutil_vsnprintf (buf, buflen, fmt, args);
674  va_end (args);
675  return len;
676}
677
678/*
679 * Copy src to string dst of size siz. At most siz-1 characters
680 * will be copied. Always NUL terminates (unless siz == 0).
681 * Returns strlen (src); if retval >= siz, truncation occurred.
682 */
683size_t
684tr_strlcpy (char * dst, const void * src, size_t siz)
685{
686#ifdef HAVE_STRLCPY
687  return strlcpy (dst, src, siz);
688#else
689  char *      d = dst;
690  const char *s = src;
691  size_t      n = siz;
692
693  assert (s);
694  assert (d);
695
696  /* Copy as many bytes as will fit */
697  if (n != 0)
698    {
699      while (--n != 0)
700        {
701          if ((*d++ = *s++) == '\0')
702            break;
703        }
704    }
705
706  /* Not enough room in dst, add NUL and traverse rest of src */
707  if (n == 0)
708    {
709      if (siz != 0)
710        *d = '\0'; /* NUL-terminate dst */
711      while (*s++)
712        ;
713    }
714
715  return s - (char*)src - 1;  /* count does not include NUL */
716#endif
717}
718
719/***
720****
721***/
722
723double
724tr_getRatio (uint64_t numerator, uint64_t denominator)
725{
726  double ratio;
727
728  if (denominator > 0)
729    ratio = numerator / (double)denominator;
730  else if (numerator > 0)
731    ratio = TR_RATIO_INF;
732  else
733    ratio = TR_RATIO_NA;
734
735  return ratio;
736}
737
738void
739tr_sha1_to_hex (char * out, const uint8_t * sha1)
740{
741  int i;
742  static const char hex[] = "0123456789abcdef";
743
744  for (i=0; i<20; ++i)
745    {
746      const unsigned int val = *sha1++;
747      *out++ = hex[val >> 4];
748      *out++ = hex[val & 0xf];
749    }
750
751  *out = '\0';
752}
753
754void
755tr_hex_to_sha1 (uint8_t * out, const char * in)
756{
757  int i;
758  static const char hex[] = "0123456789abcdef";
759
760  for (i=0; i<20; ++i)
761    {
762      const int hi = strchr (hex, tolower (*in++)) - hex;
763      const int lo = strchr (hex, tolower (*in++)) - hex;
764      *out++ = (uint8_t)((hi<<4) | lo);
765    }
766}
767
768/***
769****
770***/
771
772static bool
773isValidURLChars (const char * url, int url_len)
774{
775  const char * c;
776  const char * end;
777  static const char * rfc2396_valid_chars =
778    "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
779    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
780    "0123456789"                 /* digit */
781    "-_.!~*'()"                  /* mark */
782    ";/?:@&=+$,"                 /* reserved */
783    "<>#%<\""                    /* delims */
784    "{}|\\^[]`";                 /* unwise */
785
786  if (url == NULL)
787    return false;
788
789  for (c=url, end=c+url_len; c && *c && c!=end; ++c)
790    if (!strchr (rfc2396_valid_chars, *c))
791      return false;
792
793  return true;
794}
795
796/** @brief return true if the URL is a http or https or UDP one that Transmission understands */
797bool
798tr_urlIsValidTracker (const char * url)
799{
800  bool valid;
801
802  if (url == NULL)
803    {
804      valid = false;
805    }
806  else
807    {
808      const int len = strlen (url);
809
810      valid = isValidURLChars (url, len)
811           && !tr_urlParse (url, len, NULL, NULL, NULL, NULL)
812           && (!memcmp (url,"http://",7) || !memcmp (url,"https://",8) || !memcmp (url,"udp://",6));
813    }
814
815  return valid;
816}
817
818/** @brief return true if the URL is a http or https or ftp or sftp one that Transmission understands */
819bool
820tr_urlIsValid (const char * url, int url_len)
821{
822  bool valid;
823
824  if (url == NULL)
825    {
826      valid = false;
827    }
828  else
829    {
830      if (url_len < 0)
831        url_len = strlen (url);
832
833      valid = isValidURLChars (url, url_len)
834           && !tr_urlParse (url, url_len, NULL, NULL, NULL, NULL)
835           && (!memcmp (url,"http://",7) || !memcmp (url,"https://",8) || !memcmp (url,"ftp://",6) || !memcmp (url,"sftp://",7));
836    }
837
838  return valid;
839}
840
841bool
842tr_addressIsIP (const char * str)
843{
844  tr_address tmp;
845  return tr_address_from_string (&tmp, str);
846}
847
848int
849tr_urlParse (const char * url_in,
850             int          len,
851             char **      setme_protocol,
852             char **      setme_host,
853             int *        setme_port,
854             char **      setme_path)
855{
856  int err;
857  int port = 0;
858  int n;
859  char * tmp;
860  char * pch;
861  size_t host_len;
862  size_t protocol_len;
863  const char * host = NULL;
864  const char * protocol = NULL;
865  const char * path = NULL;
866
867  tmp = tr_strndup (url_in, len);
868  if ((pch = strstr (tmp, "://")))
869    {
870      *pch = '\0';
871      protocol = tmp;
872      protocol_len = pch - protocol;
873      pch += 3;
874      if ((n = strcspn (pch, ":/")))
875        {
876          const int havePort = pch[n] == ':';
877          host = pch;
878          host_len = n;
879          pch += n;
880          if (pch && *pch)
881            *pch++ = '\0';
882          if (havePort)
883            {
884              char * end;
885              port = strtol (pch, &end, 10);
886              pch = end;
887            }
888          path = pch;
889        }
890    }
891
892  err = !host || !path || !protocol;
893
894  if (!err && !port)
895    {
896      if (!strcmp (protocol, "udp")) port = 80;
897      else if (!strcmp (protocol, "ftp")) port = 21;
898      else if (!strcmp (protocol, "sftp")) port = 22;
899      else if (!strcmp (protocol, "http")) port = 80;
900      else if (!strcmp (protocol, "https")) port = 443;
901    }
902
903  if (!err)
904    {
905      if (setme_protocol) *setme_protocol = tr_strndup (protocol, protocol_len);
906
907      if (setme_host){ ((char*)host)[-3] = ':'; *setme_host =
908                        tr_strndup (host, host_len); }
909
910      if (setme_path){ if (!*path) *setme_path = tr_strdup ("/");
911                       else if (path[0] == '/') *setme_path = tr_strdup (path);
912                       else { ((char*)path)[-1] = '/'; *setme_path = tr_strdup (path - 1); } }
913
914      if (setme_port) *setme_port = port;
915    }
916
917
918  tr_free (tmp);
919  return err;
920}
921
922#include <string.h>
923#include <openssl/sha.h>
924#include <openssl/hmac.h>
925#include <openssl/evp.h>
926#include <openssl/bio.h>
927#include <openssl/buffer.h>
928
929char *
930tr_base64_encode (const void * input, int length, int * setme_len)
931{
932  int retlen = 0;
933  char * ret = NULL;
934
935  if (input != NULL)
936    {
937      BIO * b64;
938      BIO * bmem;
939      BUF_MEM * bptr;
940
941      if (length < 1)
942        length = (int)strlen (input);
943
944      bmem = BIO_new (BIO_s_mem ());
945      b64 = BIO_new (BIO_f_base64 ());
946      BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
947      b64 = BIO_push (b64, bmem);
948      BIO_write (b64, input, length);
949      (void) BIO_flush (b64);
950      BIO_get_mem_ptr (b64, &bptr);
951      ret = tr_strndup (bptr->data, bptr->length);
952      retlen = bptr->length;
953      BIO_free_all (b64);
954    }
955
956  if (setme_len)
957    *setme_len = retlen;
958
959  return ret;
960}
961
962char *
963tr_base64_decode (const void * input,
964                  int          length,
965                  int *        setme_len)
966{
967  char * ret;
968  BIO * b64;
969  BIO * bmem;
970  int retlen;
971
972  if (length < 1)
973    length = strlen (input);
974
975  ret = tr_new0 (char, length);
976  b64 = BIO_new (BIO_f_base64 ());
977  bmem = BIO_new_mem_buf ((unsigned char*)input, length);
978  bmem = BIO_push (b64, bmem);
979  retlen = BIO_read (bmem, ret, length);
980  if (!retlen)
981    {
982      /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
983      BIO_free_all (bmem);
984      b64 = BIO_new (BIO_f_base64 ());
985      BIO_set_flags (b64, BIO_FLAGS_BASE64_NO_NL);
986      bmem = BIO_new_mem_buf ((unsigned char*)input, length);
987      bmem = BIO_push (b64, bmem);
988      retlen = BIO_read (bmem, ret, length);
989    }
990
991  if (setme_len)
992    *setme_len = retlen;
993
994  BIO_free_all (bmem);
995  return ret;
996}
997
998/***
999****
1000***/
1001
1002void
1003tr_removeElementFromArray (void         * array,
1004                           unsigned int   index_to_remove,
1005                           size_t         sizeof_element,
1006                           size_t         nmemb)
1007{
1008  char * a = array;
1009
1010  memmove (a + sizeof_element * index_to_remove,
1011           a + sizeof_element * (index_to_remove  + 1),
1012           sizeof_element * (--nmemb - index_to_remove));
1013}
1014
1015int
1016tr_lowerBound (const void * key,
1017               const void * base,
1018               size_t       nmemb,
1019               size_t       size,
1020               int     (* compar)(const void* key, const void* arrayMember),
1021               bool       * exact_match)
1022{
1023  size_t first = 0;
1024  const char * cbase = base;
1025  bool exact = false;
1026
1027  while (nmemb != 0)
1028    {
1029      const size_t half = nmemb / 2;
1030      const size_t middle = first + half;
1031      const int c = compar (key, cbase + size*middle);
1032
1033      if (c <= 0)
1034        {
1035          if (c == 0)
1036            exact = true;
1037          nmemb = half;
1038        }
1039      else
1040        {
1041          first = middle + 1;
1042          nmemb = nmemb - half - 1;
1043        }
1044    }
1045
1046  *exact_match = exact;
1047  return first;
1048}
1049
1050/***
1051****
1052****
1053***/
1054
1055/* Byte-wise swap two items of size SIZE.
1056   From glibc, written by Douglas C. Schmidt, LGPL 2.1 or higher */
1057#define SWAP(a, b, size) \
1058  do { \
1059    register size_t __size = (size); \
1060    register char *__a = (a), *__b = (b); \
1061    if (__a != __b) do { \
1062      char __tmp = *__a; \
1063      *__a++ = *__b; \
1064      *__b++ = __tmp; \
1065    } while (--__size > 0); \
1066  } while (0)
1067
1068
1069static size_t
1070quickfindPartition (char * base, size_t left, size_t right, size_t size,
1071                    int (*compar)(const void *, const void *), size_t pivotIndex)
1072{
1073  size_t i;
1074  size_t storeIndex;
1075
1076  /* move pivot to the end */
1077  SWAP (base+(size*pivotIndex), base+(size*right), size);
1078
1079  storeIndex = left;
1080  for (i=left; i<=right-1; ++i)
1081    {
1082      if (compar (base+(size*i), base+(size*right)) <= 0)
1083        {
1084          SWAP (base+(size*storeIndex), base+(size*i), size);
1085          ++storeIndex;
1086        }
1087    }
1088
1089  /* move pivot to its final place */
1090  SWAP (base+(size*right), base+(size*storeIndex), size);
1091
1092  /* sanity check the partition */
1093#ifndef NDEBUG
1094  assert (storeIndex >= left);
1095  assert (storeIndex <= right);
1096
1097  for (i=left; i<storeIndex; ++i)
1098    assert (compar (base+(size*i), base+(size*storeIndex)) <= 0);
1099  for (i=storeIndex+1; i<=right; ++i)
1100    assert (compar (base+(size*i), base+(size*storeIndex)) >= 0);
1101#endif
1102
1103  return storeIndex;
1104}
1105
1106static void
1107quickfindFirstK (char * base, size_t left, size_t right, size_t size,
1108                 int (*compar)(const void *, const void *), size_t k)
1109{
1110  if (right > left)
1111    {
1112      const size_t pivotIndex = left + (right-left)/2u;
1113
1114      const size_t pivotNewIndex = quickfindPartition (base, left, right, size, compar, pivotIndex);
1115
1116      if (pivotNewIndex > left + k) /* new condition */
1117        quickfindFirstK (base, left, pivotNewIndex-1, size, compar, k);
1118      else if (pivotNewIndex < left + k)
1119        quickfindFirstK (base, pivotNewIndex+1, right, size, compar, k+left-pivotNewIndex-1);
1120    }
1121}
1122
1123#ifndef NDEBUG
1124static void
1125checkBestScoresComeFirst (char * base, size_t nmemb, size_t size,
1126                          int (*compar)(const void *, const void *), size_t k)
1127{
1128  size_t i;
1129  size_t worstFirstPos = 0;
1130
1131  for (i=1; i<k; ++i)
1132    if (compar (base+(size*worstFirstPos), base+(size*i)) < 0)
1133      worstFirstPos = i;
1134
1135  for (i=0; i<k; ++i)
1136    assert (compar (base+(size*i), base+(size*worstFirstPos)) <= 0);
1137  for (i=k; i<nmemb; ++i)
1138    assert (compar (base+(size*i), base+(size*worstFirstPos)) >= 0);
1139}
1140#endif
1141
1142void
1143tr_quickfindFirstK (void * base, size_t nmemb, size_t size,
1144                    int (*compar)(const void *, const void *), size_t k)
1145{
1146  if (k < nmemb)
1147    {
1148      quickfindFirstK (base, 0, nmemb-1, size, compar, k);
1149
1150#ifndef NDEBUG
1151      checkBestScoresComeFirst (base, nmemb, size, compar, k);
1152#endif
1153    }
1154}
1155
1156/***
1157****
1158***/
1159
1160static char*
1161strip_non_utf8 (const char * in, size_t inlen)
1162{
1163  const char * end;
1164  const char zero = '\0';
1165  struct evbuffer * buf = evbuffer_new ();
1166
1167  while (!tr_utf8_validate (in, inlen, &end))
1168    {
1169      const int good_len = end - in;
1170
1171      evbuffer_add (buf, in, good_len);
1172      inlen -= (good_len + 1);
1173      in += (good_len + 1);
1174      evbuffer_add (buf, "?", 1);
1175    }
1176
1177  evbuffer_add (buf, in, inlen);
1178  evbuffer_add (buf, &zero, 1);
1179  return evbuffer_free_to_str (buf);
1180}
1181
1182static char*
1183to_utf8 (const char * in, size_t inlen)
1184{
1185  char * ret = NULL;
1186
1187#ifdef HAVE_ICONV_OPEN
1188  int i;
1189  const char * encodings[] = { "CURRENT", "ISO-8859-15" };
1190  const int encoding_count = sizeof (encodings) / sizeof (encodings[1]);
1191  const size_t buflen = inlen*4 + 10;
1192  char * out = tr_new (char, buflen);
1193
1194  for (i=0; !ret && i<encoding_count; ++i)
1195    {
1196      char * inbuf = (char*) in;
1197      char * outbuf = out;
1198      size_t inbytesleft = inlen;
1199      size_t outbytesleft = buflen;
1200      const char * test_encoding = encodings[i];
1201
1202      iconv_t cd = iconv_open ("UTF-8", test_encoding);
1203      if (cd != (iconv_t)-1)
1204        {
1205          if (iconv (cd, &inbuf, &inbytesleft, &outbuf, &outbytesleft) != (size_t)-1)
1206            ret = tr_strndup (out, buflen-outbytesleft);
1207          iconv_close (cd);
1208        }
1209    }
1210
1211  tr_free (out);
1212#endif
1213
1214  if (ret == NULL)
1215    ret = strip_non_utf8 (in, inlen);
1216
1217  return ret;
1218}
1219
1220char*
1221tr_utf8clean (const char * str, int max_len)
1222{
1223  char * ret;
1224  const char * end;
1225
1226  if (max_len < 0)
1227    max_len = (int) strlen (str);
1228
1229  if (tr_utf8_validate (str, max_len, &end))
1230    ret = tr_strndup (str, max_len);
1231  else
1232    ret = to_utf8 (str, max_len);
1233
1234  assert (tr_utf8_validate (ret, -1, NULL));
1235  return ret;
1236}
1237
1238#ifdef WIN32
1239
1240char *
1241tr_win32_native_to_utf8 (const wchar_t * text,
1242                         int             text_size)
1243{
1244  char * ret = NULL;
1245  int size;
1246
1247  size = WideCharToMultiByte (CP_UTF8, 0, text, text_size, NULL, 0, NULL, NULL);
1248  if (size == 0)
1249    goto fail;
1250
1251  ret = tr_new (char, size + 1);
1252  size = WideCharToMultiByte (CP_UTF8, 0, text, text_size, ret, size, NULL, NULL);
1253  if (size == 0)
1254    goto fail;
1255
1256  ret[size] = '\0';
1257
1258  return ret;
1259
1260fail:
1261  tr_free (ret);
1262
1263  return NULL;
1264}
1265
1266wchar_t *
1267tr_win32_utf8_to_native (const char * text,
1268                         int          text_size)
1269{
1270  return tr_win32_utf8_to_native_ex (text, text_size, 0);
1271}
1272
1273wchar_t *
1274tr_win32_utf8_to_native_ex (const char * text,
1275                            int          text_size,
1276                            int          extra_chars)
1277{
1278  wchar_t * ret = NULL;
1279  int size;
1280
1281  size = MultiByteToWideChar (CP_UTF8, 0, text, text_size, NULL, 0);
1282  if (size == 0)
1283    goto fail;
1284
1285  ret = tr_new (wchar_t, size + extra_chars + 1);
1286  size = MultiByteToWideChar (CP_UTF8, 0, text, text_size, ret, size);
1287  if (size == 0)
1288    goto fail;
1289
1290  ret[size] = L'\0';
1291
1292  return ret;
1293
1294fail:
1295  tr_free (ret);
1296
1297  return NULL;
1298}
1299
1300char *
1301tr_win32_format_message (uint32_t code)
1302{
1303  wchar_t * wide_text = NULL;
1304  DWORD wide_size;
1305  char * text = NULL;
1306  size_t text_size;
1307
1308  wide_size = FormatMessageW (FORMAT_MESSAGE_ALLOCATE_BUFFER |
1309                              FORMAT_MESSAGE_FROM_SYSTEM |
1310                              FORMAT_MESSAGE_IGNORE_INSERTS,
1311                              NULL, code, 0, (LPWSTR)&wide_text, 0, NULL);
1312
1313  if (wide_size != 0 && wide_text != NULL)
1314    text = tr_win32_native_to_utf8 (wide_text, wide_size);
1315
1316  LocalFree (wide_text);
1317
1318  /* Most (all?) messages contain "\r\n" in the end, chop it */
1319  text_size = strlen (text);
1320  while (text_size > 0 &&
1321         text[text_size - 1] >= '\0' &&
1322         text[text_size - 1] <= ' ')
1323    text[--text_size] = '\0';
1324
1325  return text;
1326}
1327
1328#endif
1329
1330/***
1331****
1332***/
1333
1334struct number_range
1335{
1336  int low;
1337  int high;
1338};
1339
1340/**
1341 * This should be a single number (ex. "6") or a range (ex. "6-9").
1342 * Anything else is an error and will return failure.
1343 */
1344static bool
1345parseNumberSection (const char * str, int len, struct number_range * setme)
1346{
1347  long a, b;
1348  bool success;
1349  char * end;
1350  const int error = errno;
1351  char * tmp = tr_strndup (str, len);
1352
1353  errno = 0;
1354  a = b = strtol (tmp, &end, 10);
1355  if (errno || (end == tmp))
1356    {
1357      success = false;
1358    }
1359  else if (*end != '-')
1360    {
1361      success = true;
1362    }
1363  else
1364    {
1365      const char * pch = end + 1;
1366      b = strtol (pch, &end, 10);
1367      if (errno || (pch == end))
1368        success = false;
1369      else if (*end) /* trailing data */
1370        success = false;
1371      else
1372        success = true;
1373    }
1374
1375  tr_free (tmp);
1376
1377  setme->low = MIN (a, b);
1378  setme->high = MAX (a, b);
1379
1380  errno = error;
1381  return success;
1382}
1383
1384int
1385compareInt (const void * va, const void * vb)
1386{
1387  const int a = * (const int *)va;
1388  const int b = * (const int *)vb;
1389  return a - b;
1390}
1391
1392/**
1393 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1394 * array of setmeCount ints of all the values in the array.
1395 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1396 * It's the caller's responsibility to call tr_free () on the returned array.
1397 * If a fragment of the string can't be parsed, NULL is returned.
1398 */
1399int*
1400tr_parseNumberRange (const char * str_in, int len, int * setmeCount)
1401{
1402  int n = 0;
1403  int * uniq = NULL;
1404  char * str = tr_strndup (str_in, len);
1405  const char * walk;
1406  tr_list * ranges = NULL;
1407  bool success = true;
1408
1409  walk = str;
1410  while (walk && *walk && success)
1411    {
1412      struct number_range range;
1413      const char * pch = strchr (walk, ',');
1414      if (pch)
1415        {
1416          success = parseNumberSection (walk, pch-walk, &range);
1417          walk = pch + 1;
1418        }
1419      else
1420        {
1421          success = parseNumberSection (walk, strlen (walk), &range);
1422          walk += strlen (walk);
1423        }
1424      if (success)
1425        tr_list_append (&ranges, tr_memdup (&range, sizeof (struct number_range)));
1426    }
1427
1428  if (!success)
1429    {
1430      *setmeCount = 0;
1431      uniq = NULL;
1432    }
1433  else
1434    {
1435      int i;
1436      int n2;
1437      tr_list * l;
1438      int * sorted = NULL;
1439
1440      /* build a sorted number array */
1441      n = n2 = 0;
1442      for (l=ranges; l!=NULL; l=l->next)
1443        {
1444          const struct number_range * r = l->data;
1445          n += r->high + 1 - r->low;
1446        }
1447      sorted = tr_new (int, n);
1448      if (sorted == NULL)
1449        {
1450          n = 0;
1451          uniq = NULL;
1452        }
1453      else
1454        {
1455          for (l=ranges; l!=NULL; l=l->next)
1456            {
1457              int i;
1458              const struct number_range * r = l->data;
1459              for (i=r->low; i<=r->high; ++i)
1460                sorted[n2++] = i;
1461            }
1462          qsort (sorted, n, sizeof (int), compareInt);
1463          assert (n == n2);
1464
1465          /* remove duplicates */
1466          uniq = tr_new (int, n);
1467          if (uniq == NULL)
1468            {
1469              n = 0;
1470            }
1471          else
1472            {
1473              for (i=n=0; i<n2; ++i)
1474                if (!n || uniq[n-1] != sorted[i])
1475                  uniq[n++] = sorted[i];
1476            }
1477
1478          tr_free (sorted);
1479        }
1480    }
1481
1482  /* cleanup */
1483  tr_list_free (&ranges, tr_free);
1484  tr_free (str);
1485
1486  /* return the result */
1487  *setmeCount = n;
1488  return uniq;
1489}
1490
1491/***
1492****
1493***/
1494
1495double
1496tr_truncd (double x, int precision)
1497{
1498  char * pt;
1499  char buf[128];
1500  const int max_precision = (int) log10 (1.0 / DBL_EPSILON) - 1;
1501  tr_snprintf (buf, sizeof (buf), "%.*f", max_precision, x);
1502  if ((pt = strstr (buf, localeconv ()->decimal_point)))
1503    pt[precision ? precision+1 : 0] = '\0';
1504  return atof (buf);
1505}
1506
1507/* return a truncated double as a string */
1508static char*
1509tr_strtruncd (char * buf, double x, int precision, size_t buflen)
1510{
1511  tr_snprintf (buf, buflen, "%.*f", precision, tr_truncd (x, precision));
1512  return buf;
1513}
1514
1515char*
1516tr_strpercent (char * buf, double x, size_t buflen)
1517{
1518  if (x < 100.0)
1519    tr_strtruncd (buf, x, 1, buflen);
1520  else
1521    tr_strtruncd (buf, x, 0, buflen);
1522
1523  return buf;
1524}
1525
1526char*
1527tr_strratio (char * buf, size_t buflen, double ratio, const char * infinity)
1528{
1529  if ((int)ratio == TR_RATIO_NA)
1530    tr_strlcpy (buf, _("None"), buflen);
1531  else if ((int)ratio == TR_RATIO_INF)
1532    tr_strlcpy (buf, infinity, buflen);
1533  else
1534    tr_strpercent (buf, ratio, buflen);
1535
1536  return buf;
1537}
1538
1539/***
1540****
1541***/
1542
1543int
1544tr_moveFile (const char * oldpath, const char * newpath, bool * renamed)
1545{
1546  tr_sys_file_t in;
1547  tr_sys_file_t out;
1548  char * buf;
1549  tr_sys_path_info info;
1550  uint64_t bytesLeft;
1551  const size_t buflen = 1024 * 128; /* 128 KiB buffer */
1552  tr_error * error = NULL;
1553
1554  /* make sure the old file exists */
1555  if (!tr_sys_path_get_info (oldpath, 0, &info, &error))
1556    {
1557      const int err = error->code;
1558      tr_error_free (error);
1559      errno = err;
1560      return -1;
1561    }
1562  if (info.type != TR_SYS_PATH_IS_FILE)
1563    {
1564      errno = ENOENT;
1565      return -1;
1566    }
1567  bytesLeft = info.size;
1568
1569  /* make sure the target directory exists */
1570  {
1571    char * newdir = tr_sys_path_dirname (newpath, NULL);
1572    int i = tr_mkdirp (newdir, 0777);
1573    tr_free (newdir);
1574    if (i)
1575      return i;
1576  }
1577
1578  /* they might be on the same filesystem... */
1579  {
1580    const bool i = tr_sys_path_rename (oldpath, newpath, NULL);
1581    if (renamed != NULL)
1582      *renamed = i;
1583    if (i)
1584      return 0;
1585  }
1586
1587  /* copy the file */
1588  in = tr_sys_file_open (oldpath, TR_SYS_FILE_READ | TR_SYS_FILE_SEQUENTIAL, 0, NULL);
1589  out = tr_sys_file_open (newpath, TR_SYS_FILE_WRITE | TR_SYS_FILE_CREATE | TR_SYS_FILE_TRUNCATE, 0666, NULL);
1590  buf = tr_valloc (buflen);
1591  while (bytesLeft > 0)
1592    {
1593      const uint64_t bytesThisPass = MIN (bytesLeft, buflen);
1594      uint64_t numRead, bytesWritten;
1595      if (!tr_sys_file_read (in, buf, bytesThisPass, &numRead, NULL))
1596        break;
1597      if (!tr_sys_file_write (out, buf, numRead, &bytesWritten, NULL))
1598        break;
1599      bytesLeft -= bytesWritten;
1600    }
1601
1602  /* cleanup */
1603  tr_free (buf);
1604  tr_sys_file_close (out, NULL);
1605  tr_sys_file_close (in, NULL);
1606  if (bytesLeft != 0)
1607    return -1;
1608
1609  tr_sys_path_remove (oldpath, NULL);
1610  return 0;
1611}
1612
1613/***
1614****
1615***/
1616
1617void*
1618tr_valloc (size_t bufLen)
1619{
1620  size_t allocLen;
1621  void * buf = NULL;
1622  static size_t pageSize = 0;
1623
1624  if (!pageSize)
1625    {
1626#ifdef HAVE_GETPAGESIZE
1627      pageSize = (size_t) getpagesize ();
1628#else /* guess */
1629      pageSize = 4096;
1630#endif
1631    }
1632
1633  allocLen = pageSize;
1634  while (allocLen < bufLen)
1635    allocLen += pageSize;
1636
1637#ifdef HAVE_POSIX_MEMALIGN
1638  if (!buf)
1639    if (posix_memalign (&buf, pageSize, allocLen))
1640      buf = NULL; /* just retry with valloc/malloc */
1641#endif
1642#ifdef HAVE_VALLOC
1643  if (!buf)
1644    buf = valloc (allocLen);
1645#endif
1646  if (!buf)
1647    buf = tr_malloc (allocLen);
1648
1649  return buf;
1650}
1651
1652/***
1653****
1654***/
1655
1656uint64_t
1657tr_htonll (uint64_t x)
1658{
1659#ifdef HAVE_HTONLL
1660  return htonll (x);
1661#else
1662  /* fallback code by bdonlan at
1663   * http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c/875505#875505 */
1664  union { uint32_t lx[2]; uint64_t llx; } u;
1665  u.lx[0] = htonl (x >> 32);
1666  u.lx[1] = htonl (x & 0xFFFFFFFFULL);
1667  return u.llx;
1668#endif
1669}
1670
1671uint64_t
1672tr_ntohll (uint64_t x)
1673{
1674#ifdef HAVE_NTOHLL
1675  return ntohll (x);
1676#else
1677  /* fallback code by bdonlan at
1678   * http://stackoverflow.com/questions/809902/64-bit-ntohl-in-c/875505#875505 */
1679  union { uint32_t lx[2]; uint64_t llx; } u;
1680  u.llx = x;
1681  return ((uint64_t)ntohl (u.lx[0]) << 32) | (uint64_t)ntohl (u.lx[1]);
1682#endif
1683}
1684
1685/***
1686****
1687****
1688****
1689***/
1690
1691struct formatter_unit
1692{
1693  char * name;
1694  int64_t value;
1695};
1696
1697struct formatter_units
1698{
1699  struct formatter_unit units[4];
1700};
1701
1702enum { TR_FMT_KB, TR_FMT_MB, TR_FMT_GB, TR_FMT_TB };
1703
1704static void
1705formatter_init (struct formatter_units * units,
1706                unsigned int kilo,
1707                const char * kb, const char * mb,
1708                const char * gb, const char * tb)
1709{
1710  uint64_t value;
1711
1712  value = kilo;
1713  units->units[TR_FMT_KB].name = tr_strdup (kb);
1714  units->units[TR_FMT_KB].value = value;
1715
1716  value *= kilo;
1717  units->units[TR_FMT_MB].name = tr_strdup (mb);
1718  units->units[TR_FMT_MB].value = value;
1719
1720  value *= kilo;
1721  units->units[TR_FMT_GB].name = tr_strdup (gb);
1722  units->units[TR_FMT_GB].value = value;
1723
1724  value *= kilo;
1725  units->units[TR_FMT_TB].name = tr_strdup (tb);
1726  units->units[TR_FMT_TB].value = value;
1727}
1728
1729static char*
1730formatter_get_size_str (const struct formatter_units * u,
1731                        char * buf, int64_t bytes, size_t buflen)
1732{
1733  int precision;
1734  double value;
1735  const char * units;
1736  const struct formatter_unit * unit;
1737
1738       if (bytes < u->units[1].value) unit = &u->units[0];
1739  else if (bytes < u->units[2].value) unit = &u->units[1];
1740  else if (bytes < u->units[3].value) unit = &u->units[2];
1741  else                                unit = &u->units[3];
1742
1743  value = (double)bytes / unit->value;
1744  units = unit->name;
1745
1746  if (unit->value == 1)
1747    precision = 0;
1748  else if (value < 100)
1749    precision = 2;
1750  else
1751    precision = 1;
1752
1753  tr_snprintf (buf, buflen, "%.*f %s", precision, value, units);
1754  return buf;
1755}
1756
1757static struct formatter_units size_units;
1758
1759void
1760tr_formatter_size_init (unsigned int kilo,
1761                        const char * kb, const char * mb,
1762                        const char * gb, const char * tb)
1763{
1764  formatter_init (&size_units, kilo, kb, mb, gb, tb);
1765}
1766
1767char*
1768tr_formatter_size_B (char * buf, int64_t bytes, size_t buflen)
1769{
1770  return formatter_get_size_str (&size_units, buf, bytes, buflen);
1771}
1772
1773static struct formatter_units speed_units;
1774
1775unsigned int tr_speed_K = 0u;
1776
1777void
1778tr_formatter_speed_init (unsigned int kilo,
1779                         const char * kb, const char * mb,
1780                         const char * gb, const char * tb)
1781{
1782  tr_speed_K = kilo;
1783  formatter_init (&speed_units, kilo, kb, mb, gb, tb);
1784}
1785
1786char*
1787tr_formatter_speed_KBps (char * buf, double KBps, size_t buflen)
1788{
1789  const double K = speed_units.units[TR_FMT_KB].value;
1790  double speed = KBps;
1791
1792  if (speed <= 999.95) /* 0.0 KB to 999.9 KB */
1793    {
1794      tr_snprintf (buf, buflen, "%d %s", (int)speed, speed_units.units[TR_FMT_KB].name);
1795    }
1796  else
1797    {
1798      speed /= K;
1799
1800      if (speed <= 99.995) /* 0.98 MB to 99.99 MB */
1801        tr_snprintf (buf, buflen, "%.2f %s", speed, speed_units.units[TR_FMT_MB].name);
1802      else if (speed <= 999.95) /* 100.0 MB to 999.9 MB */
1803        tr_snprintf (buf, buflen, "%.1f %s", speed, speed_units.units[TR_FMT_MB].name);
1804      else
1805        tr_snprintf (buf, buflen, "%.1f %s", speed/K, speed_units.units[TR_FMT_GB].name);
1806    }
1807
1808  return buf;
1809}
1810
1811static struct formatter_units mem_units;
1812
1813unsigned int tr_mem_K = 0u;
1814
1815void
1816tr_formatter_mem_init (unsigned int kilo,
1817                       const char * kb, const char * mb,
1818                       const char * gb, const char * tb)
1819{
1820  tr_mem_K = kilo;
1821  formatter_init (&mem_units, kilo, kb, mb, gb, tb);
1822}
1823
1824char*
1825tr_formatter_mem_B (char * buf, int64_t bytes_per_second, size_t buflen)
1826{
1827  return formatter_get_size_str (&mem_units, buf, bytes_per_second, buflen);
1828}
1829
1830void
1831tr_formatter_get_units (void * vdict)
1832{
1833  int i;
1834  tr_variant * l;
1835  tr_variant * dict = vdict;
1836
1837  tr_variantDictReserve (dict, 6);
1838
1839  tr_variantDictAddInt (dict, TR_KEY_memory_bytes, mem_units.units[TR_FMT_KB].value);
1840  l = tr_variantDictAddList (dict, TR_KEY_memory_units, 4);
1841  for (i=0; i<4; i++)
1842    tr_variantListAddStr (l, mem_units.units[i].name);
1843
1844  tr_variantDictAddInt (dict, TR_KEY_size_bytes,   size_units.units[TR_FMT_KB].value);
1845  l = tr_variantDictAddList (dict, TR_KEY_size_units, 4);
1846  for (i=0; i<4; i++)
1847    tr_variantListAddStr (l, size_units.units[i].name);
1848
1849  tr_variantDictAddInt (dict, TR_KEY_speed_bytes,  speed_units.units[TR_FMT_KB].value);
1850  l = tr_variantDictAddList (dict, TR_KEY_speed_units, 4);
1851  for (i=0; i<4; i++)
1852    tr_variantListAddStr (l, speed_units.units[i].name);
1853}
1854
Note: See TracBrowser for help on using the repository browser.