source: trunk/libtransmission/utils.c @ 13301

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

silence an unused-variable compiler warning introduced in the previous commit

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