source: trunk/libtransmission/utils.c @ 13300

Last change on this file since 13300 was 13300, checked in by jordan, 9 years ago

(trunk libT) #4894 -- don't use evbuffer_add_printf() and evbuffer_pullup() together.

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