source: trunk/libtransmission/utils.c @ 14382

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

Fix compilation on Windows

This should not affect non-Win32 platforms in any way.
As for Win32 (both MinGW and MSVC), this should hopefully allow for
unpatched compilation. Correct functioning is not yet guaranteed though.

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