source: trunk/libtransmission/utils.c @ 14336

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

(trunk) #4160: mike.dld patch: 4160-07-env.patch

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