source: trunk/libtransmission/utils.c @ 14368

Last change on this file since 14368 was 14368, checked in by mikedld, 8 years ago

Fix tr_moveFile error reporting

Remove unused renamed argument.
Use tr_error instead of errno to report errors to support Win32 file
wrappers which do not map Windows error codes to POSIX ones.
Return bool instead of int (0/-1).

Uncomment tr_error_prefix and tr_error_propagate_prefixed functions.

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