source: trunk/libtransmission/utils.c

Last change on this file was 14718, checked in by mikedld, 7 years ago

Explicitly compare result of str(n)cmp/memcmp to signify that it's not boolean

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