source: trunk/libtransmission/utils.c @ 8247

Last change on this file since 8247 was 8247, checked in by charles, 14 years ago

(trunk libT) avoid unnecessary malloc()s & free()s in tr_utf8clean()

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