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