source: trunk/libtransmission/utils.c @ 12210

Last change on this file since 12210 was 12210, checked in by jordan, 11 years ago

(trunk libT) tr_strtruncd() is only used in one place, so make it a private function there instead of leaving it public in utils.h

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