source: trunk/libtransmission/utils.c @ 8686

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

(trunk libT) avoid an unnecessary malloc/free in tr_strdup_printf()

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