2 * This file Copyright (C) Mnemosyne LLC
4 * This file is licensed by the GPL version 2. Works owned by the
5 * Transmission project are granted a special exemption to clause 2(b)
6 * so that the bulk of its code can remain under the MIT license.
7 * This exemption does not extend to derived works not owned by
8 * the Transmission project.
10 * $Id: utils.c 12551 2011-07-17 14:15:02Z jordan $
14 #define _GNU_SOURCE /* glibc's string.h needs this to pick up memmem */
17 #if defined(SYS_DARWIN)
18 #define HAVE_GETPAGESIZE
19 #define HAVE_ICONV_OPEN
21 #undef HAVE_POSIX_MEMALIGN /* not supported on OS X 10.5 and lower */
25 #include <ctype.h> /* isdigit(), isalpha(), tolower() */
27 #include <float.h> /* DBL_EPSILON */
28 #include <locale.h> /* localeconv() */
29 #include <math.h> /* pow(), fabs(), floor() */
33 #include <string.h> /* strerror(), memset(), memmem() */
34 #include <time.h> /* nanosleep() */
36 #ifdef HAVE_ICONV_OPEN
39 #include <libgen.h> /* basename() */
41 #include <sys/types.h>
43 #include <unistd.h> /* stat(), getcwd(), getpagesize(), unlink() */
45 #include <event2/buffer.h>
46 #include <event2/event.h>
50 #define WINVER WindowsXP /* freeaddrinfo(), getaddrinfo(), getnameinfo() */
51 #include <direct.h> /* _getcwd() */
52 #include <windows.h> /* Sleep() */
55 #include "transmission.h"
58 #include "ConvertUTF.h"
61 #include "platform.h" /* tr_lockLock(), TR_PATH_MAX */
65 time_t __tr_current_time
= 0;
66 tr_msg_level __tr_message_level
= TR_MSG_ERR
;
68 static bool messageQueuing
= false;
69 static tr_msg_list
* messageQueue
= NULL
;
70 static tr_msg_list
** messageQueueTail
= &messageQueue
;
71 static int messageQueueCount
= 0;
74 /* make null versions of these win32 functions */
75 static inline int IsDebuggerPresent( void ) { return false; }
76 static inline void OutputDebugString( const void * unused UNUSED
) { }
84 getMessageLock( void )
86 static tr_lock
* l
= NULL
;
97 static bool initialized
= false;
98 static FILE * file
= NULL
;
102 const char * str
= getenv( "TR_DEBUG_FD" );
109 file
= stdout
; break;
112 file
= stderr
; break;
124 tr_setMessageLevel( tr_msg_level level
)
126 __tr_message_level
= level
;
130 tr_setMessageQueuing( bool enabled
)
132 messageQueuing
= enabled
;
136 tr_getMessageQueuing( void )
138 return messageQueuing
!= 0;
142 tr_getQueuedMessages( void )
145 tr_lockLock( getMessageLock( ) );
149 messageQueueTail
= &messageQueue
;
151 messageQueueCount
= 0;
153 tr_lockUnlock( getMessageLock( ) );
158 tr_freeMessageList( tr_msg_list
* list
)
162 while( NULL
!= list
)
165 free( list
->message
);
177 tr_localtime_r( const time_t *_clock
, struct tm
*_result
)
179 #ifdef HAVE_LOCALTIME_R
180 return localtime_r( _clock
, _result
);
182 struct tm
*p
= localtime( _clock
);
190 tr_getLogTimeStr( char * buf
, int buflen
)
198 gettimeofday( &tv
, NULL
);
201 tr_localtime_r( &seconds
, &now_tm
);
202 strftime( tmp
, sizeof( tmp
), "%H:%M:%S", &now_tm
);
203 milliseconds
= tv
.tv_usec
/ 1000;
204 tr_snprintf( buf
, buflen
, "%s.%03d", tmp
, milliseconds
);
210 tr_deepLoggingIsActive( void )
212 static int8_t deepLoggingIsActive
= -1;
214 if( deepLoggingIsActive
< 0 )
215 deepLoggingIsActive
= IsDebuggerPresent() || (tr_getLog()!=NULL
);
217 return deepLoggingIsActive
!= 0;
221 tr_deepLog( const char * file
,
227 FILE * fp
= tr_getLog( );
228 if( fp
|| IsDebuggerPresent( ) )
232 struct evbuffer
* buf
= evbuffer_new( );
233 char * base
= tr_basename( file
);
235 evbuffer_add_printf( buf
, "[%s] ",
236 tr_getLogTimeStr( timestr
, sizeof( timestr
) ) );
238 evbuffer_add_printf( buf
, "%s ", name
);
239 va_start( args
, fmt
);
240 evbuffer_add_vprintf( buf
, fmt
, args
);
242 evbuffer_add_printf( buf
, " (%s:%d)\n", base
, line
);
243 /* FIXME(libevent2) ifdef this out for nonwindows platforms */
244 OutputDebugString( evbuffer_pullup( buf
, -1 ) );
246 fputs( (const char*)evbuffer_pullup( buf
, -1 ), fp
);
249 evbuffer_free( buf
);
258 tr_msg( const char * file
, int line
,
261 const char * fmt
, ... )
263 const int err
= errno
; /* message logging shouldn't affect errno */
266 tr_lockLock( getMessageLock( ) );
268 /* build the text message */
271 evutil_vsnprintf( buf
, sizeof( buf
), fmt
, ap
);
274 OutputDebugString( buf
);
280 tr_msg_list
* newmsg
;
281 newmsg
= tr_new0( tr_msg_list
, 1 );
282 newmsg
->level
= level
;
283 newmsg
->when
= tr_time( );
284 newmsg
->message
= tr_strdup( buf
);
287 newmsg
->name
= tr_strdup( name
);
289 *messageQueueTail
= newmsg
;
290 messageQueueTail
= &newmsg
->next
;
293 if( messageQueueCount
> TR_MAX_MSG_LOG
)
295 tr_msg_list
* old
= messageQueue
;
296 messageQueue
= old
->next
;
298 tr_freeMessageList(old
);
302 assert( messageQueueCount
== TR_MAX_MSG_LOG
);
314 tr_getLogTimeStr( timestr
, sizeof( timestr
) );
317 fprintf( fp
, "[%s] %s: %s\n", timestr
, name
, buf
);
319 fprintf( fp
, "[%s] %s\n", timestr
, buf
);
324 tr_lockUnlock( getMessageLock( ) );
333 tr_malloc( size_t size
)
335 return size
? malloc( size
) : NULL
;
339 tr_malloc0( size_t size
)
341 return size
? calloc( 1, size
) : NULL
;
352 tr_memdup( const void * src
, size_t byteCount
)
354 return memcpy( tr_malloc( byteCount
), src
, byteCount
);
362 tr_strip_positional_args( const char* str
)
364 const char * in
= str
;
365 static size_t bufsize
= 0;
366 static char * buf
= NULL
;
367 const size_t len
= str
? strlen( str
) : 0;
370 if( !buf
|| ( bufsize
< len
) )
372 bufsize
= len
* 2 + 1;
373 buf
= tr_renew( char, buf
, bufsize
);
376 for( out
= buf
; str
&& *str
; ++str
)
380 if( ( *str
== '%' ) && isdigit( str
[1] ) )
382 const char * tmp
= str
+ 1;
383 while( isdigit( *tmp
) )
386 str
= tmp
[1]=='\'' ? tmp
+1 : tmp
;
389 if( ( *str
== '%' ) && ( str
[1] == '\'' ) )
395 return !in
|| strcmp( buf
, in
) ? buf
: in
;
403 tr_timerAdd( struct event
* timer
, int seconds
, int microseconds
)
407 tv
.tv_usec
= microseconds
;
409 assert( tv
.tv_sec
>= 0 );
410 assert( tv
.tv_usec
>= 0 );
411 assert( tv
.tv_usec
< 1000000 );
413 evtimer_add( timer
, &tv
);
417 tr_timerAddMsec( struct event
* timer
, int msec
)
419 const int seconds
= msec
/ 1000;
420 const int usec
= (msec
%1000) * 1000;
421 tr_timerAdd( timer
, seconds
, usec
);
429 tr_loadFile( const char * path
,
436 const char * const err_fmt
= _( "Couldn't read \"%1$s\": %2$s" );
438 /* try to stat the file */
440 if( stat( path
, &sb
) )
442 const int err
= errno
;
443 tr_dbg( err_fmt
, path
, tr_strerror( errno
) );
448 if( ( sb
.st_mode
& S_IFMT
) != S_IFREG
)
450 tr_err( err_fmt
, path
, _( "Not a regular file" ) );
455 /* Load the torrent file into our buffer */
456 fd
= tr_open_file_for_scanning( path
);
459 const int err
= errno
;
460 tr_err( err_fmt
, path
, tr_strerror( errno
) );
464 buf
= tr_malloc( sb
.st_size
+ 1 );
467 const int err
= errno
;
468 tr_err( err_fmt
, path
, _( "Memory allocation failed" ) );
473 n
= read( fd
, buf
, (size_t)sb
.st_size
);
476 const int err
= errno
;
477 tr_err( err_fmt
, path
, tr_strerror( errno
) );
485 buf
[ sb
.st_size
] = '\0';
491 tr_basename( const char * path
)
493 char * tmp
= tr_strdup( path
);
494 char * ret
= tr_strdup( basename( tmp
) );
500 tr_dirname( const char * path
)
502 char * tmp
= tr_strdup( path
);
503 char * ret
= tr_strdup( dirname( tmp
) );
509 tr_mkdir( const char * path
,
517 if( path
&& isalpha( path
[0] ) && path
[1] == ':' && !path
[2] )
519 return mkdir( path
);
521 return mkdir( path
, permissions
);
526 tr_mkdirp( const char * path_in
,
529 char * path
= tr_strdup( path_in
);
534 /* walk past the root */
536 while( *p
== TR_PATH_DELIMITER
)
542 strchr( pp
, TR_PATH_DELIMITER
) ) || ( p
= strchr( pp
, '\0' ) ) )
549 if( stat( path
, &sb
) )
551 /* Folder doesn't exist yet */
552 if( tr_mkdir( path
, permissions
) )
554 const int err
= errno
;
556 "Couldn't create \"%1$s\": %2$s" ), path
,
557 tr_strerror( err
) );
563 else if( ( sb
.st_mode
& S_IFMT
) != S_IFDIR
)
565 /* Node exists but isn't a folder */
566 char * buf
= tr_strdup_printf( _( "File \"%s\" is in the way" ), path
);
567 tr_err( _( "Couldn't create \"%1$s\": %2$s" ), path_in
, buf
);
577 *p
= TR_PATH_DELIMITER
;
587 tr_buildPath( const char *first_element
, ... )
590 const char * element
;
595 /* pass 1: allocate enough space for the string */
596 va_start( vl
, first_element
);
597 element
= first_element
;
599 bufLen
+= strlen( element
) + 1;
600 element
= va_arg( vl
, const char* );
602 pch
= buf
= tr_new( char, bufLen
);
605 /* pass 2: build the string piece by piece */
606 va_start( vl
, first_element
);
607 element
= first_element
;
609 const size_t elementLen
= strlen( element
);
610 memcpy( pch
, element
, elementLen
);
612 *pch
++ = TR_PATH_DELIMITER
;
613 element
= va_arg( vl
, const char* );
617 /* terminate the string. if nonempty, eat the unwanted trailing slash */
622 /* sanity checks & return */
623 assert( pch
- buf
== (off_t
)bufLen
);
632 evbuffer_free_to_str( struct evbuffer
* buf
)
634 const size_t n
= evbuffer_get_length( buf
);
635 char * ret
= tr_new( char, n
+ 1 );
636 evbuffer_copyout( buf
, ret
, n
);
637 evbuffer_free( buf
);
643 tr_strdup( const void * in
)
645 return tr_strndup( in
, in
? (int)strlen((const char *)in
) : 0 );
649 tr_strndup( const void * in
, int len
)
655 out
= tr_strdup( in
);
659 out
= tr_malloc( len
+ 1 );
660 memcpy( out
, in
, len
);
668 tr_memmem( const char * haystack
, size_t haystacklen
,
669 const char * needle
, size_t needlelen
)
672 return memmem( haystack
, haystacklen
, needle
, needlelen
);
677 if( needlelen
> haystacklen
|| !haystack
|| !needle
)
679 for( i
=0; i
<=haystacklen
-needlelen
; ++i
)
680 if( !memcmp( haystack
+i
, needle
, needlelen
) )
687 tr_strdup_printf( const char * fmt
, ... )
695 len
= evutil_vsnprintf( statbuf
, sizeof( statbuf
), fmt
, ap
);
697 if( len
< sizeof( statbuf
) )
698 ret
= tr_strndup( statbuf
, len
);
700 ret
= tr_new( char, len
+ 1 );
702 evutil_vsnprintf( ret
, len
+ 1, fmt
, ap
);
712 const char * ret
= strerror( i
);
715 ret
= "Unknown Error";
720 tr_strcmp0( const char * str1
, const char * str2
)
722 if( str1
&& str2
) return strcmp( str1
, str2
);
724 if( str2
) return -1;
732 /* https://bugs.launchpad.net/percona-patches/+bug/526863/+attachment/1160199/+files/solaris_10_fix.patch */
734 tr_strsep( char ** str
, const char * delims
)
737 return strsep( str
, delims
);
747 while (**str
!= '\0') {
748 if (strchr(delims
, **str
) != NULL
) {
756 /* There is not another token */
764 tr_strstrip( char * str
)
769 size_t len
= strlen( str
);
771 while( len
&& isspace( str
[len
- 1] ) )
774 for( pos
= 0; pos
< len
&& isspace( str
[pos
] ); )
778 memmove( str
, str
+ pos
, len
);
786 tr_str_has_suffix( const char *str
, const char *suffix
)
796 str_len
= strlen( str
);
797 suffix_len
= strlen( suffix
);
798 if( str_len
< suffix_len
)
801 return !evutil_ascii_strncasecmp( str
+ str_len
- suffix_len
, suffix
, suffix_len
);
813 gettimeofday( &tv
, NULL
);
814 return (uint64_t) tv
.tv_sec
* 1000 + ( tv
.tv_usec
/ 1000 );
818 tr_wait_msec( long int msec
)
821 Sleep( (DWORD
)msec
);
824 ts
.tv_sec
= msec
/ 1000;
825 ts
.tv_nsec
= ( msec
% 1000 ) * 1000000;
826 nanosleep( &ts
, NULL
);
835 tr_snprintf( char * buf
, size_t buflen
, const char * fmt
, ... )
840 va_start( args
, fmt
);
841 len
= evutil_vsnprintf( buf
, buflen
, fmt
, args
);
847 * Copy src to string dst of size siz. At most siz-1 characters
848 * will be copied. Always NUL terminates (unless siz == 0).
849 * Returns strlen(src); if retval >= siz, truncation occurred.
852 tr_strlcpy( char * dst
, const void * src
, size_t siz
)
855 return strlcpy( dst
, src
, siz
);
864 /* Copy as many bytes as will fit */
869 if( ( *d
++ = *s
++ ) == '\0' )
874 /* Not enough room in dst, add NUL and traverse rest of src */
878 *d
= '\0'; /* NUL-terminate dst */
883 return s
- (char*)src
- 1; /* count does not include NUL */
892 tr_getRatio( uint64_t numerator
, uint64_t denominator
)
896 if( denominator
> 0 )
897 ratio
= numerator
/ (double)denominator
;
898 else if( numerator
> 0 )
899 ratio
= TR_RATIO_INF
;
907 tr_sha1_to_hex( char * out
, const uint8_t * sha1
)
910 static const char hex
[] = "0123456789abcdef";
912 for( i
=0; i
<20; ++i
)
914 const unsigned int val
= *sha1
++;
915 *out
++ = hex
[val
>> 4];
916 *out
++ = hex
[val
& 0xf];
923 tr_hex_to_sha1( uint8_t * out
, const char * in
)
926 static const char hex
[] = "0123456789abcdef";
928 for( i
=0; i
<20; ++i
)
930 const int hi
= strchr( hex
, tolower( *in
++ ) ) - hex
;
931 const int lo
= strchr( hex
, tolower( *in
++ ) ) - hex
;
932 *out
++ = (uint8_t)( (hi
<<4) | lo
);
941 isValidURLChars( const char * url
, int url_len
)
945 static const char * rfc2396_valid_chars
=
946 "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
947 "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
948 "0123456789" /* digit */
949 "-_.!~*'()" /* mark */
950 ";/?:@&=+$," /* reserved */
951 "<>#%<\"" /* delims */
952 "{}|\\^[]`"; /* unwise */
957 for( c
=url
, end
=c
+url_len
; c
&& *c
&& c
!=end
; ++c
)
958 if( !strchr( rfc2396_valid_chars
, *c
) )
964 /** @brief return true if the URL is a http or https or UDP one that Transmission understands */
966 tr_urlIsValidTracker( const char * url
)
969 const int len
= url
? strlen(url
) : 0;
971 valid
= isValidURLChars( url
, len
)
972 && !tr_urlParse( url
, len
, NULL
, NULL
, NULL
, NULL
)
973 && ( !memcmp(url
,"http://",7) || !memcmp(url
,"https://",8) || !memcmp(url
,"udp://",6) );
978 /** @brief return true if the URL is a http or https or ftp or sftp one that Transmission understands */
980 tr_urlIsValid( const char * url
, int url_len
)
983 if( ( url_len
< 0 ) && ( url
!= NULL
) )
984 url_len
= strlen( url
);
986 valid
= isValidURLChars( url
, url_len
)
987 && !tr_urlParse( url
, url_len
, NULL
, NULL
, NULL
, NULL
)
988 && ( !memcmp(url
,"http://",7) || !memcmp(url
,"https://",8) || !memcmp(url
,"ftp://",6) || !memcmp(url
,"sftp://",7) );
994 tr_addressIsIP( const char * str
)
997 return tr_address_from_string( &tmp
, str
);
1001 tr_urlParse( const char * url_in
,
1003 char ** setme_protocol
,
1006 char ** setme_path
)
1014 size_t protocol_len
;
1015 const char * host
= NULL
;
1016 const char * protocol
= NULL
;
1017 const char * path
= NULL
;
1019 tmp
= tr_strndup( url_in
, len
);
1020 if( ( pch
= strstr( tmp
, "://" ) ) )
1024 protocol_len
= pch
- protocol
;
1026 /*fprintf( stderr, "protocol is [%s]... what's left is [%s]\n", protocol, pch);*/
1027 if( ( n
= strcspn( pch
, ":/" ) ) )
1029 const int havePort
= pch
[n
] == ':';
1035 /*fprintf( stderr, "host is [%s]... what's left is [%s]\n", host, pch );*/
1039 port
= strtol( pch
, &end
, 10 );
1041 /*fprintf( stderr, "port is [%d]... what's left is [%s]\n", port, pch );*/
1044 /*fprintf( stderr, "path is [%s]\n", path );*/
1048 err
= !host
|| !path
|| !protocol
;
1052 if( !strcmp( protocol
, "udp" ) ) port
= 80;
1053 else if( !strcmp( protocol
, "ftp" ) ) port
= 21;
1054 else if( !strcmp( protocol
, "sftp" ) ) port
= 22;
1055 else if( !strcmp( protocol
, "http" ) ) port
= 80;
1056 else if( !strcmp( protocol
, "https" ) ) port
= 443;
1061 if( setme_protocol
) *setme_protocol
= tr_strndup( protocol
, protocol_len
);
1063 if( setme_host
){ ( (char*)host
)[-3] = ':'; *setme_host
=
1064 tr_strndup( host
, host_len
); }
1065 if( setme_path
){ if( !*path
) *setme_path
= tr_strdup( "/" );
1066 else if( path
[0] == '/' ) *setme_path
= tr_strdup( path
);
1067 else { ( (char*)path
)[-1] = '/'; *setme_path
= tr_strdup( path
- 1 ); } }
1068 if( setme_port
) *setme_port
= port
;
1077 #include <openssl/sha.h>
1078 #include <openssl/hmac.h>
1079 #include <openssl/evp.h>
1080 #include <openssl/bio.h>
1081 #include <openssl/buffer.h>
1084 tr_base64_encode( const void * input
, int length
, int * setme_len
)
1096 length
= (int)strlen( input
);
1098 bmem
= BIO_new( BIO_s_mem( ) );
1099 b64
= BIO_new( BIO_f_base64( ) );
1100 BIO_set_flags( b64
, BIO_FLAGS_BASE64_NO_NL
);
1101 b64
= BIO_push( b64
, bmem
);
1102 BIO_write( b64
, input
, length
);
1103 (void) BIO_flush( b64
);
1104 BIO_get_mem_ptr( b64
, &bptr
);
1105 ret
= tr_strndup( bptr
->data
, bptr
->length
);
1106 retlen
= bptr
->length
;
1107 BIO_free_all( b64
);
1111 *setme_len
= retlen
;
1117 tr_base64_decode( const void * input
,
1127 length
= strlen( input
);
1129 ret
= tr_new0( char, length
);
1130 b64
= BIO_new( BIO_f_base64( ) );
1131 bmem
= BIO_new_mem_buf( (unsigned char*)input
, length
);
1132 bmem
= BIO_push( b64
, bmem
);
1133 retlen
= BIO_read( bmem
, ret
, length
);
1136 /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
1137 BIO_free_all( bmem
);
1138 b64
= BIO_new( BIO_f_base64( ) );
1139 BIO_set_flags( b64
, BIO_FLAGS_BASE64_NO_NL
);
1140 bmem
= BIO_new_mem_buf( (unsigned char*)input
, length
);
1141 bmem
= BIO_push( b64
, bmem
);
1142 retlen
= BIO_read( bmem
, ret
, length
);
1146 *setme_len
= retlen
;
1148 BIO_free_all( bmem
);
1157 tr_removeElementFromArray( void * array
,
1158 unsigned int index_to_remove
,
1159 size_t sizeof_element
,
1164 memmove( a
+ sizeof_element
* index_to_remove
,
1165 a
+ sizeof_element
* ( index_to_remove
+ 1 ),
1166 sizeof_element
* ( --nmemb
- index_to_remove
) );
1170 tr_lowerBound( const void * key
,
1174 int (* compar
)(const void* key
, const void* arrayMember
),
1175 bool * exact_match
)
1178 const char * cbase
= base
;
1183 const size_t half
= nmemb
/ 2;
1184 const size_t middle
= first
+ half
;
1185 const int c
= compar( key
, cbase
+ size
*middle
);
1193 nmemb
= nmemb
- half
- 1;
1197 *exact_match
= exact
;
1207 strip_non_utf8( const char * in
, size_t inlen
)
1210 const char zero
= '\0';
1211 struct evbuffer
* buf
= evbuffer_new( );
1213 while( !tr_utf8_validate( in
, inlen
, &end
) )
1215 const int good_len
= end
- in
;
1217 evbuffer_add( buf
, in
, good_len
);
1218 inlen
-= ( good_len
+ 1 );
1219 in
+= ( good_len
+ 1 );
1220 evbuffer_add( buf
, "?", 1 );
1223 evbuffer_add( buf
, in
, inlen
);
1224 evbuffer_add( buf
, &zero
, 1 );
1225 return evbuffer_free_to_str( buf
);
1229 to_utf8( const char * in
, size_t inlen
)
1233 #ifdef HAVE_ICONV_OPEN
1235 const char * encodings
[] = { "CURRENT", "ISO-8859-15" };
1236 const int encoding_count
= sizeof(encodings
) / sizeof(encodings
[1]);
1237 const size_t buflen
= inlen
*4 + 10;
1238 char * out
= tr_new( char, buflen
);
1240 for( i
=0; !ret
&& i
<encoding_count
; ++i
)
1242 char * inbuf
= (char*) in
;
1243 char * outbuf
= out
;
1244 size_t inbytesleft
= inlen
;
1245 size_t outbytesleft
= buflen
;
1246 const char * test_encoding
= encodings
[i
];
1248 iconv_t cd
= iconv_open( "UTF-8", test_encoding
);
1249 if( cd
!= (iconv_t
)-1 ) {
1250 if( iconv( cd
, &inbuf
, &inbytesleft
, &outbuf
, &outbytesleft
) != (size_t)-1 )
1251 ret
= tr_strndup( out
, buflen
-outbytesleft
);
1260 ret
= strip_non_utf8( in
, inlen
);
1266 tr_utf8clean( const char * str
, int max_len
)
1272 max_len
= (int) strlen( str
);
1274 if( tr_utf8_validate( str
, max_len
, &end
) )
1275 ret
= tr_strndup( str
, max_len
);
1277 ret
= to_utf8( str
, max_len
);
1279 assert( tr_utf8_validate( ret
, -1, NULL
) );
1294 * This should be a single number (ex. "6") or a range (ex. "6-9").
1295 * Anything else is an error and will return failure.
1298 parseNumberSection( const char * str
, int len
, struct number_range
* setme
)
1303 const int error
= errno
;
1304 char * tmp
= tr_strndup( str
, len
);
1307 a
= b
= strtol( tmp
, &end
, 10 );
1308 if( errno
|| ( end
== tmp
) ) {
1310 } else if( *end
!= '-' ) {
1313 const char * pch
= end
+ 1;
1314 b
= strtol( pch
, &end
, 10 );
1315 if( errno
|| ( pch
== end
) )
1317 else if( *end
) /* trailing data */
1324 setme
->low
= MIN( a
, b
);
1325 setme
->high
= MAX( a
, b
);
1332 compareInt( const void * va
, const void * vb
)
1334 const int a
= *(const int *)va
;
1335 const int b
= *(const int *)vb
;
1340 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1341 * array of setmeCount ints of all the values in the array.
1342 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1343 * It's the caller's responsibility to call tr_free() on the returned array.
1344 * If a fragment of the string can't be parsed, NULL is returned.
1347 tr_parseNumberRange( const char * str_in
, int len
, int * setmeCount
)
1351 char * str
= tr_strndup( str_in
, len
);
1353 tr_list
* ranges
= NULL
;
1354 bool success
= true;
1357 while( walk
&& *walk
&& success
) {
1358 struct number_range range
;
1359 const char * pch
= strchr( walk
, ',' );
1361 success
= parseNumberSection( walk
, pch
-walk
, &range
);
1364 success
= parseNumberSection( walk
, strlen( walk
), &range
);
1365 walk
+= strlen( walk
);
1368 tr_list_append( &ranges
, tr_memdup( &range
, sizeof( struct number_range
) ) );
1381 int * sorted
= NULL
;
1383 /* build a sorted number array */
1385 for( l
=ranges
; l
!=NULL
; l
=l
->next
) {
1386 const struct number_range
* r
= l
->data
;
1387 n
+= r
->high
+ 1 - r
->low
;
1389 sorted
= tr_new( int, n
);
1390 for( l
=ranges
; l
!=NULL
; l
=l
->next
) {
1391 const struct number_range
* r
= l
->data
;
1393 for( i
=r
->low
; i
<=r
->high
; ++i
)
1396 qsort( sorted
, n
, sizeof( int ), compareInt
);
1399 /* remove duplicates */
1400 uniq
= tr_new( int, n
);
1401 for( i
=n
=0; i
<n2
; ++i
)
1402 if( !n
|| uniq
[n
-1] != sorted
[i
] )
1403 uniq
[n
++] = sorted
[i
];
1409 tr_list_free( &ranges
, tr_free
);
1412 /* return the result */
1422 tr_truncd( double x
, int precision
)
1426 const int max_precision
= (int) log10( 1.0 / DBL_EPSILON
) - 1;
1427 tr_snprintf( buf
, sizeof( buf
), "%.*f", max_precision
, x
);
1428 if(( pt
= strstr( buf
, localeconv()->decimal_point
)))
1429 pt
[precision
? precision
+1 : 0] = '\0';
1433 /* return a truncated double as a string */
1435 tr_strtruncd( char * buf
, double x
, int precision
, size_t buflen
)
1437 tr_snprintf( buf
, buflen
, "%.*f", precision
, tr_truncd( x
, precision
) );
1442 tr_strpercent( char * buf
, double x
, size_t buflen
)
1445 tr_strtruncd( buf
, x
, 2, buflen
);
1446 else if( x
< 100.0 )
1447 tr_strtruncd( buf
, x
, 1, buflen
);
1449 tr_strtruncd( buf
, x
, 0, buflen
);
1454 tr_strratio( char * buf
, size_t buflen
, double ratio
, const char * infinity
)
1456 if( (int)ratio
== TR_RATIO_NA
)
1457 tr_strlcpy( buf
, _( "None" ), buflen
);
1458 else if( (int)ratio
== TR_RATIO_INF
)
1459 tr_strlcpy( buf
, infinity
, buflen
);
1461 tr_strpercent( buf
, ratio
, buflen
);
1470 tr_moveFile( const char * oldpath
, const char * newpath
, bool * renamed
)
1477 const size_t buflen
= 1024 * 128; /* 128 KiB buffer */
1479 /* make sure the old file exists */
1480 if( stat( oldpath
, &st
) ) {
1481 const int err
= errno
;
1485 if( !S_ISREG( st
.st_mode
) ) {
1489 bytesLeft
= st
.st_size
;
1491 /* make sure the target directory exists */
1493 char * newdir
= tr_dirname( newpath
);
1494 int i
= tr_mkdirp( newdir
, 0777 );
1500 /* they might be on the same filesystem... */
1502 const int i
= rename( oldpath
, newpath
);
1503 if( renamed
!= NULL
)
1510 in
= tr_open_file_for_scanning( oldpath
);
1511 out
= tr_open_file_for_writing( newpath
);
1512 buf
= tr_valloc( buflen
);
1513 while( bytesLeft
> 0 )
1515 ssize_t bytesWritten
;
1516 const off_t bytesThisPass
= MIN( bytesLeft
, (off_t
)buflen
);
1517 const int numRead
= read( in
, buf
, bytesThisPass
);
1520 bytesWritten
= write( out
, buf
, numRead
);
1521 if( bytesWritten
< 0 )
1523 bytesLeft
-= bytesWritten
;
1528 tr_close_file( out
);
1529 tr_close_file( in
);
1530 if( bytesLeft
!= 0 )
1538 tr_is_same_file( const char * filename1
, const char * filename2
)
1540 struct stat sb1
, sb2
;
1542 return !stat( filename1
, &sb1
)
1543 && !stat( filename2
, &sb2
)
1544 && ( sb1
.st_dev
== sb2
.st_dev
)
1545 && ( sb1
.st_ino
== sb2
.st_ino
);
1553 tr_valloc( size_t bufLen
)
1557 static size_t pageSize
= 0;
1560 #ifdef HAVE_GETPAGESIZE
1561 pageSize
= (size_t) getpagesize();
1567 allocLen
= pageSize
;
1568 while( allocLen
< bufLen
)
1569 allocLen
+= pageSize
;
1571 #ifdef HAVE_POSIX_MEMALIGN
1573 if( posix_memalign( &buf
, pageSize
, allocLen
) )
1574 buf
= NULL
; /* just retry with valloc/malloc */
1578 buf
= valloc( allocLen
);
1581 buf
= tr_malloc( allocLen
);
1587 tr_realpath( const char * path
, char * resolved_path
)
1590 /* From a message to the Mingw-msys list, Jun 2, 2005 by Mark Junker. */
1591 if( GetFullPathNameA( path
, TR_PATH_MAX
, resolved_path
, NULL
) == 0 )
1593 return resolved_path
;
1595 return realpath( path
, resolved_path
);
1604 tr_htonll( uint64_t x
)
1609 /* fallback code by Runner and Juan Carlos Cobas at
1610 * http://www.codeproject.com/KB/cpp/endianness.aspx */
1611 return (((uint64_t)(htonl((int)((x
<< 32) >> 32))) << 32) |
1612 (unsigned int)htonl(((int)(x
>> 32))));
1617 tr_ntohll( uint64_t x
)
1622 /* fallback code by Runner and Juan Carlos Cobas at
1623 * http://www.codeproject.com/KB/cpp/endianness.aspx */
1624 return (((uint64_t)(ntohl((int)((x
<< 32) >> 32))) << 32) |
1625 (unsigned int)ntohl(((int)(x
>> 32))));
1635 struct formatter_unit
1641 struct formatter_units
1643 struct formatter_unit units
[4];
1646 enum { TR_FMT_KB
, TR_FMT_MB
, TR_FMT_GB
, TR_FMT_TB
};
1649 formatter_init( struct formatter_units
* units
,
1651 const char * kb
, const char * mb
,
1652 const char * gb
, const char * tb
)
1654 uint64_t value
= kilo
;
1655 units
->units
[TR_FMT_KB
].name
= tr_strdup( kb
);
1656 units
->units
[TR_FMT_KB
].value
= value
;
1659 units
->units
[TR_FMT_MB
].name
= tr_strdup( mb
);
1660 units
->units
[TR_FMT_MB
].value
= value
;
1663 units
->units
[TR_FMT_GB
].name
= tr_strdup( gb
);
1664 units
->units
[TR_FMT_GB
].value
= value
;
1667 units
->units
[TR_FMT_TB
].name
= tr_strdup( tb
);
1668 units
->units
[TR_FMT_TB
].value
= value
;
1672 formatter_get_size_str( const struct formatter_units
* u
,
1673 char * buf
, int64_t bytes
, size_t buflen
)
1678 const struct formatter_unit
* unit
;
1680 if( bytes
< u
->units
[1].value
) unit
= &u
->units
[0];
1681 else if( bytes
< u
->units
[2].value
) unit
= &u
->units
[1];
1682 else if( bytes
< u
->units
[3].value
) unit
= &u
->units
[2];
1683 else unit
= &u
->units
[3];
1685 value
= (double)bytes
/ unit
->value
;
1687 if( unit
->value
== 1 )
1689 else if( value
< 100 )
1693 tr_snprintf( buf
, buflen
, "%.*f %s", precision
, value
, units
);
1697 static struct formatter_units size_units
;
1700 tr_formatter_size_init( unsigned int kilo
,
1701 const char * kb
, const char * mb
,
1702 const char * gb
, const char * tb
)
1704 formatter_init( &size_units
, kilo
, kb
, mb
, gb
, tb
);
1708 tr_formatter_size_B( char * buf
, int64_t bytes
, size_t buflen
)
1710 return formatter_get_size_str( &size_units
, buf
, bytes
, buflen
);
1713 static struct formatter_units speed_units
;
1715 unsigned int tr_speed_K
= 0u;
1718 tr_formatter_speed_init( unsigned int kilo
,
1719 const char * kb
, const char * mb
,
1720 const char * gb
, const char * tb
)
1723 formatter_init( &speed_units
, kilo
, kb
, mb
, gb
, tb
);
1727 tr_formatter_speed_KBps( char * buf
, double KBps
, size_t buflen
)
1729 const double K
= speed_units
.units
[TR_FMT_KB
].value
;
1730 double speed
= KBps
;
1732 if( speed
<= 999.95 ) /* 0.0 KB to 999.9 KB */
1733 tr_snprintf( buf
, buflen
, "%d %s", (int)speed
, speed_units
.units
[TR_FMT_KB
].name
);
1736 if( speed
<= 99.995 ) /* 0.98 MB to 99.99 MB */
1737 tr_snprintf( buf
, buflen
, "%.2f %s", speed
, speed_units
.units
[TR_FMT_MB
].name
);
1738 else if (speed
<= 999.95) /* 100.0 MB to 999.9 MB */
1739 tr_snprintf( buf
, buflen
, "%.1f %s", speed
, speed_units
.units
[TR_FMT_MB
].name
);
1742 tr_snprintf( buf
, buflen
, "%.1f %s", speed
, speed_units
.units
[TR_FMT_GB
].name
);
1749 static struct formatter_units mem_units
;
1751 unsigned int tr_mem_K
= 0u;
1754 tr_formatter_mem_init( unsigned int kilo
,
1755 const char * kb
, const char * mb
,
1756 const char * gb
, const char * tb
)
1759 formatter_init( &mem_units
, kilo
, kb
, mb
, gb
, tb
);
1763 tr_formatter_mem_B( char * buf
, int64_t bytes_per_second
, size_t buflen
)
1765 return formatter_get_size_str( &mem_units
, buf
, bytes_per_second
, buflen
);
1769 tr_formatter_get_units( tr_benc
* d
)
1774 tr_bencDictReserve( d
, 6 );
1776 tr_bencDictAddInt( d
, "memory-bytes", mem_units
.units
[TR_FMT_KB
].value
);
1777 l
= tr_bencDictAddList( d
, "memory-units", 4 );
1778 for( i
=0; i
<4; i
++ ) tr_bencListAddStr( l
, mem_units
.units
[i
].name
);
1780 tr_bencDictAddInt( d
, "size-bytes", size_units
.units
[TR_FMT_KB
].value
);
1781 l
= tr_bencDictAddList( d
, "size-units", 4 );
1782 for( i
=0; i
<4; i
++ ) tr_bencListAddStr( l
, size_units
.units
[i
].name
);
1784 tr_bencDictAddInt( d
, "speed-bytes", speed_units
.units
[TR_FMT_KB
].value
);
1785 l
= tr_bencDictAddList( d
, "speed-units", 4 );
1786 for( i
=0; i
<4; i
++ ) tr_bencListAddStr( l
, speed_units
.units
[i
].name
);