source: trunk/libtransmission/utils.c @ 14337

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

(trunk) #4160: mike.dld patch: 4160-08-args.patch

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