source: trunk/libtransmission/utils.c @ 8348

Last change on this file since 8348 was 8348, checked in by charles, 13 years ago

(trunk libT) add some testing messages for #2030... this dumps a lot of junk to the terminal and will be taken out as soon as there's some meaningful feedback in #2030

  • Property svn:keywords set to Date Rev Author Id
File size: 34.6 KB
Line 
1/*
2 * This file Copyright (C) 2009 Charles Kerr <charles@transmissionbt.com>
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 8348 2009-05-07 01:05:10Z charles $
11 */
12
13#include <assert.h>
14#include <ctype.h> /* isalpha, tolower */
15#include <errno.h>
16#include <stdarg.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h> /* strerror, memset */
20
21#include <libgen.h> /* basename */
22#include <sys/time.h>
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <unistd.h> /* usleep, stat, getcwd */
26
27#include "event.h"
28
29#ifdef WIN32
30 #include <direct.h> /* _getcwd */
31 #include <windows.h> /* Sleep */
32#endif
33
34#include "transmission.h"
35#include "fdlimit.h"
36#include "ConvertUTF.h"
37#include "list.h"
38#include "utils.h"
39#include "platform.h"
40#include "version.h"
41
42static tr_lock *      messageLock = NULL;
43static int            messageLevel = 0;
44static tr_bool        messageQueuing = FALSE;
45static tr_msg_list *  messageQueue = NULL;
46static tr_msg_list ** messageQueueTail = &messageQueue;
47
48#ifndef WIN32
49    /* make null versions of these win32 functions */
50    static int IsDebuggerPresent( void ) { return FALSE; }
51    static void OutputDebugString( const void * unused UNUSED ) { }
52#endif
53
54static void
55tr_msgInit( void )
56{
57    static tr_bool initialized = FALSE;
58
59    if( !initialized )
60    {
61        char * env = getenv( "TR_DEBUG" );
62        messageLevel = ( env ? atoi( env ) : 0 ) + 1;
63        messageLevel = MAX( 1, messageLevel );
64
65        messageLock = tr_lockNew( );
66
67        initialized = TRUE;
68    }
69}
70
71FILE*
72tr_getLog( void )
73{
74    static int    initialized = FALSE;
75    static FILE * file = NULL;
76
77    if( !initialized )
78    {
79        const char * str = getenv( "TR_DEBUG_FD" );
80        int          fd = 0;
81        if( str && *str )
82            fd = atoi( str );
83        switch( fd )
84        {
85            case 1:
86                file = stdout; break;
87
88            case 2:
89                file = stderr; break;
90
91            default:
92                file = NULL; break;
93        }
94        initialized = TRUE;
95    }
96
97    return file;
98}
99
100void
101tr_setMessageLevel( int level )
102{
103    tr_msgInit( );
104    tr_lockLock( messageLock );
105
106    messageLevel = MAX( 0, level );
107
108    tr_lockUnlock( messageLock );
109}
110
111int
112tr_getMessageLevel( void )
113{
114    int ret;
115    tr_msgInit( );
116    tr_lockLock( messageLock );
117
118    ret = messageLevel;
119
120    tr_lockUnlock( messageLock );
121    return ret;
122}
123
124void
125tr_setMessageQueuing( tr_bool enabled )
126{
127    tr_msgInit( );
128    tr_lockLock( messageLock );
129
130    messageQueuing = enabled;
131
132    tr_lockUnlock( messageLock );
133}
134
135tr_bool
136tr_getMessageQueuing( void )
137{
138    int ret;
139    tr_msgInit( );
140    tr_lockLock( messageLock );
141
142    ret = messageQueuing;
143
144    tr_lockUnlock( messageLock );
145    return ret;
146}
147
148tr_msg_list *
149tr_getQueuedMessages( void )
150{
151    tr_msg_list * ret;
152    tr_msgInit( );
153    tr_lockLock( messageLock );
154
155    ret = messageQueue;
156    messageQueue = NULL;
157    messageQueueTail = &messageQueue;
158
159    tr_lockUnlock( messageLock );
160    return ret;
161}
162
163void
164tr_freeMessageList( tr_msg_list * list )
165{
166    tr_msg_list * next;
167
168    while( NULL != list )
169    {
170        next = list->next;
171        free( list->message );
172        free( list->name );
173        free( list );
174        list = next;
175    }
176}
177
178/**
179***
180**/
181
182struct tm *
183tr_localtime_r( const time_t *_clock, struct tm *_result )
184{
185#ifdef HAVE_LOCALTIME_R
186    return localtime_r( _clock, _result );
187#else
188    struct tm *p = localtime( _clock );
189    if( p )
190        *(_result) = *p;
191    return p;
192#endif
193}
194
195char*
196tr_getLogTimeStr( char * buf, int buflen )
197{
198    char           tmp[64];
199    time_t         now;
200    struct tm      now_tm;
201    struct timeval tv;
202    int            milliseconds;
203
204    now = time( NULL );
205    gettimeofday( &tv, NULL );
206
207    tr_localtime_r( &now, &now_tm );
208    strftime( tmp, sizeof( tmp ), "%H:%M:%S", &now_tm );
209    milliseconds = (int)( tv.tv_usec / 1000 );
210    tr_snprintf( buf, buflen, "%s.%03d", tmp, milliseconds );
211
212    return buf;
213}
214
215void
216tr_assertImpl( const char * file, int line, const char * test, const char * fmt, ... )
217{
218    char buf[64];
219    fprintf( stderr, "[%s] Transmission %s Assertion \"%s\" failed at %s:%d.  ",
220                     tr_getLogTimeStr( buf, sizeof( buf ) ),
221                      LONG_VERSION_STRING, test, file, line );
222    if( fmt && *fmt ) {
223        va_list args;
224        fputc( '(', stderr );
225        va_start( args, fmt );
226        vfprintf( stderr, fmt, args );
227        va_end( args );
228        fputs( ")  ", stderr );
229    }
230    fputs( "Please report this bug at <http://trac.transmissionbt.com/newticket>; Thank you.\n", stderr );
231    abort( );
232}
233
234
235tr_bool
236tr_deepLoggingIsActive( void )
237{
238    static int8_t deepLoggingIsActive = -1;
239
240    if( deepLoggingIsActive < 0 )
241        deepLoggingIsActive = IsDebuggerPresent() || (tr_getLog()!=NULL);
242
243    return deepLoggingIsActive != 0;
244}
245
246void
247tr_deepLog( const char  * file,
248            int           line,
249            const char  * name,
250            const char  * fmt,
251            ... )
252{
253    FILE * fp = tr_getLog( );
254    if( fp || IsDebuggerPresent( ) )
255    {
256        va_list           args;
257        char              timestr[64];
258        struct evbuffer * buf = tr_getBuffer( );
259        char *            base = tr_basename( file );
260
261        evbuffer_add_printf( buf, "[%s] ",
262                            tr_getLogTimeStr( timestr, sizeof( timestr ) ) );
263        if( name )
264            evbuffer_add_printf( buf, "%s ", name );
265        va_start( args, fmt );
266        evbuffer_add_vprintf( buf, fmt, args );
267        va_end( args );
268        evbuffer_add_printf( buf, " (%s:%d)\n", base, line );
269        OutputDebugString( EVBUFFER_DATA( buf ) );
270        if(fp)
271            (void) fwrite( EVBUFFER_DATA( buf ), 1, EVBUFFER_LENGTH( buf ), fp );
272
273        tr_free( base );
274        tr_releaseBuffer( buf );
275    }
276}
277
278/***
279****
280***/
281   
282
283int
284tr_msgLoggingIsActive( int level )
285{
286    tr_msgInit( );
287
288    return messageLevel >= level;
289}
290
291void
292tr_msg( const char * file,
293        int          line,
294        int          level,
295        const char * name,
296        const char * fmt,
297        ... )
298{
299    FILE * fp;
300    tr_msgInit( );
301    tr_lockLock( messageLock );
302
303    fp = tr_getLog( );
304
305    if( messageLevel >= level )
306    {
307        char buf[MAX_STACK_ARRAY_SIZE];
308        va_list ap;
309
310        /* build the text message */
311        *buf = '\0';
312        va_start( ap, fmt );
313        evutil_vsnprintf( buf, sizeof( buf ), fmt, ap );
314        va_end( ap );
315
316        OutputDebugString( buf );
317
318        if( *buf )
319        {
320            if( messageQueuing )
321            {
322                tr_msg_list * newmsg;
323                newmsg = tr_new0( tr_msg_list, 1 );
324                newmsg->level = level;
325                newmsg->when = time( NULL );
326                newmsg->message = tr_strdup( buf );
327                newmsg->file = file;
328                newmsg->line = line;
329                newmsg->name = tr_strdup( name );
330
331                *messageQueueTail = newmsg;
332                messageQueueTail = &newmsg->next;
333            }
334            else
335            {
336                char timestr[64];
337
338                if( fp == NULL )
339                    fp = stderr;
340
341                tr_getLogTimeStr( timestr, sizeof( timestr ) );
342
343                if( name )
344                    fprintf( fp, "[%s] %s: %s\n", timestr, name, buf );
345                else
346                    fprintf( fp, "[%s] %s\n", timestr, buf );
347                fflush( fp );
348            }
349        }
350    }
351
352    tr_lockUnlock( messageLock );
353}
354
355/***
356****
357***/
358
359void
360tr_set_compare( const void * va,
361                size_t aCount,
362                const void * vb,
363                size_t bCount,
364                int compare( const void * a, const void * b ),
365                size_t elementSize,
366                tr_set_func in_a_cb,
367                tr_set_func in_b_cb,
368                tr_set_func in_both_cb,
369                void * userData )
370{
371    const uint8_t * a = (const uint8_t *) va;
372    const uint8_t * b = (const uint8_t *) vb;
373    const uint8_t * aend = a + elementSize * aCount;
374    const uint8_t * bend = b + elementSize * bCount;
375
376    while( a != aend || b != bend )
377    {
378        if( a == aend )
379        {
380            ( *in_b_cb )( (void*)b, userData );
381            b += elementSize;
382        }
383        else if( b == bend )
384        {
385            ( *in_a_cb )( (void*)a, userData );
386            a += elementSize;
387        }
388        else
389        {
390            const int val = ( *compare )( a, b );
391
392            if( !val )
393            {
394                ( *in_both_cb )( (void*)a, userData );
395                a += elementSize;
396                b += elementSize;
397            }
398            else if( val < 0 )
399            {
400                ( *in_a_cb )( (void*)a, userData );
401                a += elementSize;
402            }
403            else if( val > 0 )
404            {
405                ( *in_b_cb )( (void*)b, userData );
406                b += elementSize;
407            }
408        }
409    }
410}
411
412/***
413****
414***/
415
416#ifdef DISABLE_GETTEXT
417
418const char*
419tr_strip_positional_args( const char* str )
420{
421    static size_t bufsize = 0;
422    static char * buf = NULL;
423    const size_t  len = strlen( str );
424    char *        out;
425
426    if( bufsize < len )
427    {
428        bufsize = len * 2;
429        buf = tr_renew( char, buf, bufsize );
430    }
431
432    for( out = buf; *str; ++str )
433    {
434        *out++ = *str;
435        if( ( *str == '%' ) && isdigit( str[1] ) )
436        {
437            const char * tmp = str + 1;
438            while( isdigit( *tmp ) )
439                ++tmp;
440
441            if( *tmp == '$' )
442                str = tmp;
443        }
444    }
445    *out = '\0';
446
447    return buf;
448}
449
450#endif
451
452/**
453***
454**/
455
456void
457tr_timevalMsec( uint64_t milliseconds, struct timeval * setme )
458{
459    const uint64_t microseconds = milliseconds * 1000;
460    assert( setme != NULL );
461    setme->tv_sec  = microseconds / 1000000;
462    setme->tv_usec = microseconds % 1000000;
463}
464
465uint8_t *
466tr_loadFile( const char * path,
467             size_t *     size )
468{
469    uint8_t * buf;
470    struct stat  sb;
471    int fd;
472    ssize_t n;
473    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );
474
475    /* try to stat the file */
476    errno = 0;
477    if( stat( path, &sb ) )
478    {
479        const int err = errno;
480        tr_dbg( err_fmt, path, tr_strerror( errno ) );
481        errno = err;
482        return NULL;
483    }
484    tr_inf( "at the end of stat(%s), errno is %d", path, errno );
485
486    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
487    {
488        tr_err( err_fmt, path, _( "Not a regular file" ) );
489        errno = EISDIR;
490        return NULL;
491    }
492    tr_inf( "looks like %s is a regular file... now we'll try to read it", path );
493
494    /* Load the torrent file into our buffer */
495    fd = tr_open_file_for_scanning( path );
496    if( fd < 0 )
497    {
498        const int err = errno;
499        tr_err( err_fmt, path, tr_strerror( errno ) );
500        errno = err;
501        return NULL;
502    }
503    tr_inf( "after trying to open the file for reading, fd is %d and errno is %d", fd, errno );
504    buf = malloc( sb.st_size + 1 );
505    if( !buf )
506    {
507        const int err = errno;
508        tr_err( err_fmt, path, _( "Memory allocation failed" ) );
509        tr_close_file( fd );
510        errno = err;
511        return NULL;
512    }
513    n = read( fd, buf, sb.st_size );
514    tr_inf( "file size is %"PRId64"; read is %"PRId64", errno is %d", (int64_t)sb.st_size, (int64_t)n, errno );
515    if( n == -1 )
516    {
517        const int err = errno;
518        tr_err( err_fmt, path, tr_strerror( errno ) );
519        tr_close_file( fd );
520        free( buf );
521        errno = err;
522        return NULL;
523    }
524
525    tr_inf( "nearing the end, and errno is %d", errno );
526    tr_close_file( fd );
527    buf[ sb.st_size ] = '\0';
528    *size = sb.st_size;
529    return buf;
530}
531
532char*
533tr_basename( const char * path )
534{
535    char * tmp = tr_strdup( path );
536    char * ret = tr_strdup( basename( tmp ) );
537    tr_free( tmp );
538    return ret;
539}
540
541char*
542tr_dirname( const char * path )
543{
544    char * tmp = tr_strdup( path );
545    char * ret = tr_strdup( dirname( tmp ) );
546    tr_free( tmp );
547    return ret;
548}
549
550int
551tr_mkdir( const char * path,
552          int permissions
553#ifdef WIN32
554                       UNUSED
555#endif
556        )
557{
558#ifdef WIN32
559    if( path && isalpha( path[0] ) && path[1] == ':' && !path[2] )
560        return 0;
561    return mkdir( path );
562#else
563    return mkdir( path, permissions );
564#endif
565}
566
567int
568tr_mkdirp( const char * path_in,
569           int          permissions )
570{
571    char *      path = tr_strdup( path_in );
572    char *      p, * pp;
573    struct stat sb;
574    int         done;
575
576    /* walk past the root */
577    p = path;
578    while( *p == TR_PATH_DELIMITER )
579        ++p;
580
581    pp = p;
582    done = 0;
583    while( ( p =
584                strchr( pp, TR_PATH_DELIMITER ) ) || ( p = strchr( pp, '\0' ) ) )
585    {
586        if( !*p )
587            done = 1;
588        else
589            *p = '\0';
590
591        if( stat( path, &sb ) )
592        {
593            /* Folder doesn't exist yet */
594            if( tr_mkdir( path, permissions ) )
595            {
596                const int err = errno;
597                tr_err( _(
598                           "Couldn't create \"%1$s\": %2$s" ), path,
599                       tr_strerror( err ) );
600                tr_free( path );
601                errno = err;
602                return -1;
603            }
604        }
605        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
606        {
607            /* Node exists but isn't a folder */
608            char * buf = tr_strdup_printf( _( "File \"%s\" is in the way" ), path );
609            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), path_in, buf );
610            tr_free( buf );
611            tr_free( path );
612            errno = ENOTDIR;
613            return -1;
614        }
615
616        if( done )
617            break;
618
619        *p = TR_PATH_DELIMITER;
620        p++;
621        pp = p;
622    }
623
624    tr_free( path );
625    return 0;
626}
627
628char*
629tr_buildPath( const char *first_element, ... )
630{
631    size_t bufLen = 0;
632    const char * element;
633    char * buf;
634    char * pch;
635    va_list vl;
636
637    /* pass 1: allocate enough space for the string */
638    va_start( vl, first_element );
639    element = first_element;
640    while( element ) {
641        bufLen += strlen( element ) + 1;
642        element = (const char*) va_arg( vl, const char* );
643    }
644    pch = buf = tr_new( char, bufLen );
645    va_end( vl );
646
647    /* pass 2: build the string piece by piece */
648    va_start( vl, first_element );
649    element = first_element;
650    while( element ) {
651        const size_t elementLen = strlen( element );
652        memcpy( pch, element, elementLen );
653        pch += elementLen;
654        *pch++ = TR_PATH_DELIMITER;
655        element = (const char*) va_arg( vl, const char* );
656    }
657    va_end( vl );
658
659    /* terminate the string.  if nonempty, eat the unwanted trailing slash */
660    if( pch != buf )
661        --pch;
662    *pch++ = '\0';
663
664    /* sanity checks & return */
665    assert( pch - buf == (off_t)bufLen );
666    return buf;
667}
668
669/****
670*****
671****/
672
673char*
674tr_strndup( const void * in, int len )
675{
676    char * out = NULL;
677
678    if( len < 0 )
679    {
680        out = tr_strdup( in );
681    }
682    else if( in )
683    {
684        out = tr_malloc( len + 1 );
685        memcpy( out, in, len );
686        out[len] = '\0';
687    }
688
689    return out;
690}
691
692char*
693tr_strdup_printf( const char * fmt, ... )
694{
695    char *            ret = NULL;
696    struct evbuffer * buf;
697    va_list           ap;
698
699    buf = tr_getBuffer( );
700    va_start( ap, fmt );
701
702    if( evbuffer_add_vprintf( buf, fmt, ap ) != -1 )
703        ret = tr_strdup( EVBUFFER_DATA( buf ) );
704
705    va_end( ap );
706    tr_releaseBuffer( buf );
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
720/****
721*****
722****/
723
724char*
725tr_strstrip( char * str )
726{
727    if( str != NULL )
728    {
729        size_t pos;
730        size_t len = strlen( str );
731
732        while( len && isspace( str[len - 1] ) )
733            --len;
734
735        str[len] = '\0';
736
737        for( pos = 0; pos < len && isspace( str[pos] ); )
738            ++pos;
739
740        len -= pos;
741        memmove( str, str + pos, len );
742        str[len] = '\0';
743    }
744
745    return str;
746}
747
748/****
749*****
750****/
751
752tr_bitfield*
753tr_bitfieldConstruct( tr_bitfield * b, size_t bitCount )
754{
755    b->bitCount = bitCount;
756    b->byteCount = ( bitCount + 7u ) / 8u;
757    b->bits = tr_new0( uint8_t, b->byteCount );
758    return b;
759}
760
761tr_bitfield*
762tr_bitfieldDestruct( tr_bitfield * b )
763{
764    if( b )
765        tr_free( b->bits );
766    return b;
767}
768
769tr_bitfield*
770tr_bitfieldDup( const tr_bitfield * in )
771{
772    tr_bitfield * ret = tr_new0( tr_bitfield, 1 );
773
774    ret->bitCount = in->bitCount;
775    ret->byteCount = in->byteCount;
776    ret->bits = tr_memdup( in->bits, in->byteCount );
777    return ret;
778}
779
780void
781tr_bitfieldClear( tr_bitfield * bitfield )
782{
783    memset( bitfield->bits, 0, bitfield->byteCount );
784}
785
786int
787tr_bitfieldIsEmpty( const tr_bitfield * bitfield )
788{
789    size_t i;
790
791    for( i = 0; i < bitfield->byteCount; ++i )
792        if( bitfield->bits[i] )
793            return 0;
794
795    return 1;
796}
797
798int
799tr_bitfieldAdd( tr_bitfield * bitfield,
800                size_t        nth )
801{
802    assert( bitfield );
803    assert( bitfield->bits );
804
805    if( nth >= bitfield->bitCount )
806        return -1;
807
808    bitfield->bits[nth >> 3u] |= ( 0x80 >> ( nth & 7u ) );
809    return 0;
810}
811
812/* Sets bit range [begin, end) to 1 */
813int
814tr_bitfieldAddRange( tr_bitfield * b,
815                     size_t        begin,
816                     size_t        end )
817{
818    size_t        sb, eb;
819    unsigned char sm, em;
820
821    end--;
822
823    if( ( end >= b->bitCount ) || ( begin > end ) )
824        return -1;
825
826    sb = begin >> 3;
827    sm = ~( 0xff << ( 8 - ( begin & 7 ) ) );
828    eb = end >> 3;
829    em = 0xff << ( 7 - ( end & 7 ) );
830
831    if( sb == eb )
832    {
833        b->bits[sb] |= ( sm & em );
834    }
835    else
836    {
837        b->bits[sb] |= sm;
838        b->bits[eb] |= em;
839        if( ++sb < eb )
840            memset ( b->bits + sb, 0xff, eb - sb );
841    }
842
843    return 0;
844}
845
846int
847tr_bitfieldRem( tr_bitfield * bitfield,
848                size_t        nth )
849{
850    assert( bitfield );
851    assert( bitfield->bits );
852
853    if( nth >= bitfield->bitCount )
854        return -1;
855
856    bitfield->bits[nth >> 3u] &= ( 0xff7f >> ( nth & 7u ) );
857    return 0;
858}
859
860/* Clears bit range [begin, end) to 0 */
861int
862tr_bitfieldRemRange( tr_bitfield * b,
863                     size_t        begin,
864                     size_t        end )
865{
866    size_t        sb, eb;
867    unsigned char sm, em;
868
869    end--;
870
871    if( ( end >= b->bitCount ) || ( begin > end ) )
872        return -1;
873
874    sb = begin >> 3;
875    sm = 0xff << ( 8 - ( begin & 7 ) );
876    eb = end >> 3;
877    em = ~( 0xff << ( 7 - ( end & 7 ) ) );
878
879    if( sb == eb )
880    {
881        b->bits[sb] &= ( sm | em );
882    }
883    else
884    {
885        b->bits[sb] &= sm;
886        b->bits[eb] &= em;
887        if( ++sb < eb )
888            memset ( b->bits + sb, 0, eb - sb );
889    }
890
891    return 0;
892}
893
894tr_bitfield*
895tr_bitfieldOr( tr_bitfield *       a,
896               const tr_bitfield * b )
897{
898    uint8_t *      ait;
899    const uint8_t *aend, *bit;
900
901    assert( a->bitCount == b->bitCount );
902
903    for( ait = a->bits, bit = b->bits, aend = ait + a->byteCount;
904         ait != aend; )
905        *ait++ |= *bit++;
906
907    return a;
908}
909
910/* set 'a' to all the flags that were in 'a' but not 'b' */
911void
912tr_bitfieldDifference( tr_bitfield *       a,
913                       const tr_bitfield * b )
914{
915    uint8_t *      ait;
916    const uint8_t *aend, *bit;
917
918    assert( a->bitCount == b->bitCount );
919
920    for( ait = a->bits, bit = b->bits, aend = ait + a->byteCount;
921         ait != aend; )
922        *ait++ &= ~( *bit++ );
923}
924
925size_t
926tr_bitfieldCountTrueBits( const tr_bitfield* b )
927{
928    size_t           ret = 0;
929    const uint8_t *  it, *end;
930    static const int trueBitCount[512] = {
931        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3,
932        4, 2, 3, 3, 4, 3, 4, 4, 5,
933        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
934        5, 3, 4, 4, 5, 4, 5, 5, 6,
935        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
936        5, 3, 4, 4, 5, 4, 5, 5, 6,
937        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5,
938        6, 4, 5, 5, 6, 5, 6, 6, 7,
939        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
940        5, 3, 4, 4, 5, 4, 5, 5, 6,
941        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5,
942        6, 4, 5, 5, 6, 5, 6, 6, 7,
943        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5,
944        6, 4, 5, 5, 6, 5, 6, 6, 7,
945        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6,
946        7, 5, 6, 6, 7, 6, 7, 7, 8,
947        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4,
948        5, 3, 4, 4, 5, 4, 5, 5, 6,
949        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5,
950        6, 4, 5, 5, 6, 5, 6, 6, 7,
951        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5,
952        6, 4, 5, 5, 6, 5, 6, 6, 7,
953        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6,
954        7, 5, 6, 6, 7, 6, 7, 7, 8,
955        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5,
956        6, 4, 5, 5, 6, 5, 6, 6, 7,
957        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6,
958        7, 5, 6, 6, 7, 6, 7, 7, 8,
959        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6,
960        7, 5, 6, 6, 7, 6, 7, 7, 8,
961        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, 5, 6, 6, 7, 6, 7, 7,
962        8, 6, 7, 7, 8, 7, 8, 8, 9
963    };
964
965    if( !b )
966        return 0;
967
968    for( it = b->bits, end = it + b->byteCount; it != end; ++it )
969        ret += trueBitCount[*it];
970
971    return ret;
972}
973
974/***
975****
976***/
977
978uint64_t
979tr_date( void )
980{
981    struct timeval tv;
982
983    gettimeofday( &tv, NULL );
984    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
985}
986
987void
988tr_wait( uint64_t delay_milliseconds )
989{
990#ifdef WIN32
991    Sleep( (DWORD)delay_milliseconds );
992#else
993    usleep( 1000 * delay_milliseconds );
994#endif
995}
996
997/***
998****
999***/
1000
1001int
1002tr_snprintf( char *       buf,
1003             size_t       buflen,
1004             const char * fmt,
1005             ... )
1006{
1007    int     len;
1008    va_list args;
1009
1010    va_start( args, fmt );
1011    len = evutil_vsnprintf( buf, buflen, fmt, args );
1012    va_end( args );
1013    return len;
1014}
1015
1016/*
1017 * Copy src to string dst of size siz.  At most siz-1 characters
1018 * will be copied.  Always NUL terminates (unless siz == 0).
1019 * Returns strlen(src); if retval >= siz, truncation occurred.
1020 */
1021size_t
1022tr_strlcpy( char *       dst,
1023            const void * src,
1024            size_t       siz )
1025{
1026#ifdef HAVE_STRLCPY
1027    return strlcpy( dst, src, siz );
1028#else
1029    char *      d = dst;
1030    const char *s = src;
1031    size_t      n = siz;
1032
1033    assert( s );
1034    assert( d );
1035
1036    /* Copy as many bytes as will fit */
1037    if( n != 0 )
1038    {
1039        while( --n != 0 )
1040        {
1041            if( ( *d++ = *s++ ) == '\0' )
1042                break;
1043        }
1044    }
1045
1046    /* Not enough room in dst, add NUL and traverse rest of src */
1047    if( n == 0 )
1048    {
1049        if( siz != 0 )
1050            *d = '\0'; /* NUL-terminate dst */
1051        while( *s++ )
1052            ;
1053    }
1054
1055    return s - (char*)src - 1;  /* count does not include NUL */
1056#endif
1057}
1058
1059/***
1060****
1061***/
1062
1063double
1064tr_getRatio( double numerator,
1065             double denominator )
1066{
1067    double ratio;
1068
1069    if( denominator )
1070        ratio = numerator / denominator;
1071    else if( numerator )
1072        ratio = TR_RATIO_INF;
1073    else
1074        ratio = TR_RATIO_NA;
1075
1076    return ratio;
1077}
1078
1079void
1080tr_sha1_to_hex( char *          out,
1081                const uint8_t * sha1 )
1082{
1083    static const char hex[] = "0123456789abcdef";
1084    int               i;
1085
1086    for( i = 0; i < 20; i++ )
1087    {
1088        unsigned int val = *sha1++;
1089        *out++ = hex[val >> 4];
1090        *out++ = hex[val & 0xf];
1091    }
1092    *out = '\0';
1093}
1094
1095/***
1096****
1097***/
1098
1099int
1100tr_httpIsValidURL( const char * url )
1101{
1102    const char *        c;
1103    static const char * rfc2396_valid_chars =
1104        "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
1105        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
1106        "0123456789"                 /* digit */
1107        "-_.!~*'()"                  /* mark */
1108        ";/?:@&=+$,"                 /* reserved */
1109        "<>#%<\""                    /* delims */
1110        "{}|\\^[]`";                 /* unwise */
1111
1112    if( url == NULL )
1113        return FALSE;
1114
1115    for( c = url; c && *c; ++c )
1116        if( !strchr( rfc2396_valid_chars, *c ) )
1117            return FALSE;
1118
1119    return !tr_httpParseURL( url, -1, NULL, NULL, NULL );
1120}
1121
1122int
1123tr_httpParseURL( const char * url_in,
1124                 int          len,
1125                 char **      setme_host,
1126                 int *        setme_port,
1127                 char **      setme_path )
1128{
1129    int          err;
1130    int          port = 0;
1131    int          n;
1132    char *       tmp;
1133    char *       pch;
1134    const char * protocol = NULL;
1135    const char * host = NULL;
1136    const char * path = NULL;
1137
1138    tmp = tr_strndup( url_in, len );
1139    if( ( pch = strstr( tmp, "://" ) ) )
1140    {
1141        *pch = '\0';
1142        protocol = tmp;
1143        pch += 3;
1144/*fprintf( stderr, "protocol is [%s]... what's left is [%s]\n", protocol, pch
1145  );*/
1146        if( ( n = strcspn( pch, ":/" ) ) )
1147        {
1148            const int havePort = pch[n] == ':';
1149            host = pch;
1150            pch += n;
1151            *pch++ = '\0';
1152/*fprintf( stderr, "host is [%s]... what's left is [%s]\n", host, pch );*/
1153            if( havePort )
1154            {
1155                char * end;
1156                port = strtol( pch, &end, 10 );
1157                pch = end;
1158/*fprintf( stderr, "port is [%d]... what's left is [%s]\n", port, pch );*/
1159            }
1160            path = pch;
1161/*fprintf( stderr, "path is [%s]\n", path );*/
1162        }
1163    }
1164
1165    err = !host || !path || !protocol
1166          || ( strcmp( protocol, "http" ) && strcmp( protocol, "https" ) );
1167
1168    if( !err && !port )
1169    {
1170        if( !strcmp( protocol, "http" ) ) port = 80;
1171        if( !strcmp( protocol, "https" ) ) port = 443;
1172    }
1173
1174    if( !err )
1175    {
1176        if( setme_host ){ ( (char*)host )[-3] = ':'; *setme_host =
1177                              tr_strdup( protocol ); }
1178        if( setme_path ){ ( (char*)path )[-1] = '/'; *setme_path =
1179                              tr_strdup( path - 1 ); }
1180        if( setme_port ) *setme_port = port;
1181    }
1182
1183
1184    tr_free( tmp );
1185    return err;
1186}
1187
1188#include <string.h>
1189#include <openssl/sha.h>
1190#include <openssl/hmac.h>
1191#include <openssl/evp.h>
1192#include <openssl/bio.h>
1193#include <openssl/buffer.h>
1194
1195char *
1196tr_base64_encode( const void * input,
1197                  int          length,
1198                  int *        setme_len )
1199{
1200    char *    ret;
1201    BIO *     b64;
1202    BIO *     bmem;
1203    BUF_MEM * bptr;
1204
1205    if( length < 1 )
1206        length = strlen( input );
1207
1208    bmem = BIO_new( BIO_s_mem( ) );
1209    b64 = BIO_new( BIO_f_base64( ) );
1210    b64 = BIO_push( b64, bmem );
1211    BIO_write( b64, input, length );
1212    (void) BIO_flush( b64 );
1213    BIO_get_mem_ptr( b64, &bptr );
1214    ret = tr_strndup( bptr->data, bptr->length );
1215    if( setme_len )
1216        *setme_len = bptr->length;
1217
1218    BIO_free_all( b64 );
1219    return ret;
1220}
1221
1222char *
1223tr_base64_decode( const void * input,
1224                  int          length,
1225                  int *        setme_len )
1226{
1227    char * ret;
1228    BIO *  b64;
1229    BIO *  bmem;
1230    int    retlen;
1231
1232    if( length < 1 )
1233        length = strlen( input );
1234
1235    ret = tr_new0( char, length );
1236    b64 = BIO_new( BIO_f_base64( ) );
1237    bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1238    bmem = BIO_push( b64, bmem );
1239    retlen = BIO_read( bmem, ret, length );
1240    if( !retlen )
1241    {
1242        /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
1243        BIO_free_all( bmem );
1244        b64 = BIO_new( BIO_f_base64( ) );
1245        BIO_set_flags( b64, BIO_FLAGS_BASE64_NO_NL );
1246        bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1247        bmem = BIO_push( b64, bmem );
1248        retlen = BIO_read( bmem, ret, length );
1249    }
1250
1251    if( setme_len )
1252        *setme_len = retlen;
1253
1254    BIO_free_all( bmem );
1255    return ret;
1256}
1257
1258int
1259tr_ptr2int( void* v )
1260{
1261    return (intptr_t)v;
1262}
1263
1264void*
1265tr_int2ptr( int i )
1266{
1267    return (void*)(intptr_t)i;
1268}
1269
1270/***
1271****
1272***/
1273
1274static tr_list * _bufferList = NULL;
1275
1276static tr_lock *
1277getBufferLock( void )
1278{
1279    static tr_lock * lock = NULL;
1280    if( lock == NULL )
1281        lock = tr_lockNew( );
1282    return lock;
1283}
1284
1285struct evbuffer*
1286tr_getBuffer( void )
1287{
1288    struct evbuffer * buf;
1289    tr_lock * l = getBufferLock( );
1290    tr_lockLock( l );
1291
1292    buf = tr_list_pop_front( &_bufferList );
1293    if( buf == NULL )
1294        buf = evbuffer_new( );
1295
1296    tr_lockUnlock( l );
1297    return buf;
1298}
1299
1300void
1301tr_releaseBuffer( struct evbuffer * buf )
1302{
1303    tr_lock * l = getBufferLock( );
1304    tr_lockLock( l );
1305
1306    evbuffer_drain( buf, EVBUFFER_LENGTH( buf ) );
1307    assert( EVBUFFER_LENGTH( buf ) == 0 );
1308    tr_list_prepend( &_bufferList, buf );
1309
1310    tr_lockUnlock( l );
1311}
1312
1313/***
1314****
1315***/
1316
1317int
1318tr_lowerBound( const void * key,
1319               const void * base,
1320               size_t       nmemb,
1321               size_t       size,
1322               int       (* compar)(const void* key, const void* arrayMember),
1323               tr_bool    * exact_match )
1324{
1325    size_t first = 0;
1326    const char * cbase = base;
1327
1328    while( nmemb )
1329    {
1330        const size_t half = nmemb / 2;
1331        const size_t middle = first + half;
1332        const int c = compar( key, cbase + size*middle );
1333
1334        if( c < 0 )
1335        {
1336            first = middle + 1;
1337            nmemb = nmemb - half - 1;
1338        }
1339        else if( !c )
1340        {
1341            if( exact_match )
1342                *exact_match = TRUE;
1343            return middle;
1344        }
1345        else
1346        {
1347            nmemb = half;
1348        }
1349    }
1350
1351    if( exact_match )
1352        *exact_match = FALSE;
1353
1354    return first;
1355}
1356
1357/***
1358****
1359***/
1360
1361char*
1362tr_utf8clean( const char * str, int max_len, tr_bool * err )
1363{
1364    char * ret;
1365    const char * end;
1366
1367    if( max_len < 0 )
1368        max_len = (int) strlen( str );
1369
1370    if( err != NULL )
1371        *err = FALSE;
1372
1373    if( tr_utf8_validate( str, max_len, &end  ) )
1374    {
1375        ret = tr_strndup( str, max_len );
1376    }
1377    else
1378    {
1379        const char zero = '\0';
1380        struct evbuffer * buf = evbuffer_new( );
1381
1382        while( !tr_utf8_validate ( str, max_len, &end ) )
1383        {
1384            const int good_len = end - str;
1385
1386            evbuffer_add( buf, str, good_len );
1387            max_len -= ( good_len + 1 );
1388            str += ( good_len + 1 );
1389            evbuffer_add( buf, "?", 1 );
1390
1391            if( err != NULL )
1392                *err = TRUE;
1393        }
1394
1395        evbuffer_add( buf, str, max_len );
1396        evbuffer_add( buf, &zero, 1 );
1397        ret = tr_memdup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
1398        evbuffer_free( buf );
1399    }
1400
1401    assert( tr_utf8_validate( ret, -1, NULL ) );
1402    return ret;
1403}
1404
1405/***
1406****
1407***/
1408
1409struct number_range
1410{
1411    int low;
1412    int high;
1413};
1414
1415/**
1416 * This should be a single number (ex. "6") or a range (ex. "6-9").
1417 * Anything else is an error and will return failure.
1418 */
1419static tr_bool
1420parseNumberSection( const char * str, int len, struct number_range * setme )
1421{
1422    long a, b;
1423    tr_bool success;
1424    char * end;
1425    const int error = errno;
1426    char * tmp = tr_strndup( str, len );
1427
1428    errno = 0;
1429    a = b = strtol( tmp, &end, 10 );
1430    if( errno || ( end == tmp ) ) {
1431        success = FALSE;
1432    } else if( *end != '-' ) {
1433        b = a;
1434        success = TRUE;
1435    } else {
1436        const char * pch = end + 1;
1437        b = strtol( pch, &end, 10 );
1438        if( errno || ( pch == end ) )
1439            success = FALSE;
1440        else if( *end ) /* trailing data */
1441            success = FALSE;
1442        else
1443            success = TRUE;
1444    }
1445    tr_free( tmp );
1446
1447    setme->low = MIN( a, b );
1448    setme->high = MAX( a, b );
1449
1450    errno = error;
1451    return success;
1452}
1453
1454static int
1455compareInt( const void * va, const void * vb )
1456{
1457    const int a = *(const int *)va;
1458    const int b = *(const int *)vb;
1459    return a - b;
1460}
1461
1462/**
1463 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1464 * array of setmeCount ints of all the values in the array.
1465 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1466 * It's the caller's responsibility to call tr_free() on the returned array.
1467 * If a fragment of the string can't be parsed, NULL is returned.
1468 */
1469int*
1470tr_parseNumberRange( const char * str_in, int len, int * setmeCount )
1471{
1472    int n = 0;
1473    int * uniq = NULL;
1474    char * str = tr_strndup( str_in, len );
1475    const char * walk;
1476    tr_list * ranges = NULL;
1477    tr_bool success = TRUE;
1478
1479    walk = str;
1480    while( walk && *walk && success ) {
1481        struct number_range range;
1482        const char * pch = strchr( walk, ',' );
1483        if( pch ) {
1484            success = parseNumberSection( walk, pch-walk, &range );
1485            walk = pch + 1;
1486        } else {
1487            success = parseNumberSection( walk, strlen( walk ), &range );
1488            walk += strlen( walk );
1489        }
1490        if( success )
1491            tr_list_append( &ranges, tr_memdup( &range, sizeof( struct number_range ) ) );
1492    }
1493
1494    if( !success )
1495    {
1496        *setmeCount = 0;
1497        uniq = NULL;
1498    }
1499    else
1500    {
1501        int i;
1502        int n2;
1503        tr_list * l;
1504        int * sorted = NULL;
1505
1506        /* build a sorted number array */
1507        n = n2 = 0;
1508        for( l=ranges; l!=NULL; l=l->next ) {
1509            const struct number_range * r = l->data;
1510            n += r->high + 1 - r->low;
1511        }
1512        sorted = tr_new( int, n );
1513        for( l=ranges; l!=NULL; l=l->next ) {
1514            const struct number_range * r = l->data;
1515            int i;
1516            for( i=r->low; i<=r->high; ++i )
1517                sorted[n2++] = i;
1518        }
1519        qsort( sorted, n, sizeof( int ), compareInt );
1520        assert( n == n2 );
1521
1522        /* remove duplicates */
1523        uniq = tr_new( int, n );
1524        for( i=n=0; i<n2; ++i )
1525            if( !n || uniq[n-1] != sorted[i] )
1526                uniq[n++] = sorted[i];
1527
1528        tr_free( sorted );
1529    }
1530
1531    /* cleanup */
1532    tr_list_free( &ranges, tr_free );
1533    tr_free( str );
1534
1535    /* return the result */
1536    *setmeCount = n;
1537    return uniq;
1538}
1539
1540/***
1541****
1542***/
1543
1544static void
1545printf_double_without_rounding( char * buf, int buflen, double d, int places )
1546{
1547    char * pch;
1548    char tmp[128];
1549    int len;
1550    tr_snprintf( tmp, sizeof( tmp ), "%'.64f", d );
1551    pch = tmp;
1552    while( isdigit( *pch ) ) ++pch; /* walk to the decimal point */
1553    ++pch; /* walk over the decimal point */
1554    pch += places;
1555    len = MIN( buflen - 1, pch - tmp );
1556    memcpy( buf, tmp, len );
1557    buf[len] = '\0';
1558}
1559
1560char*
1561tr_strratio( char * buf, size_t buflen, double ratio, const char * infinity )
1562{
1563    if( (int)ratio == TR_RATIO_NA )
1564        tr_strlcpy( buf, _( "None" ), buflen );
1565    else if( (int)ratio == TR_RATIO_INF )
1566        tr_strlcpy( buf, infinity, buflen );
1567    else if( ratio < 10.0 )
1568        printf_double_without_rounding( buf, buflen, ratio, 2 );
1569    else if( ratio < 100.0 )
1570        printf_double_without_rounding( buf, buflen, ratio, 1 );
1571    else
1572        tr_snprintf( buf, buflen, "%'.0f", ratio );
1573    return buf;
1574}
Note: See TracBrowser for help on using the repository browser.