source: trunk/libtransmission/utils.c @ 8449

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

(trunk libT) make tr_memmem() more readable

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