source: trunk/libtransmission/utils.c @ 8695

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

(trunk libT) move tr_bitfield into its own, package-visible source files. this may break the mac build temporarily until the xcode file gets synced

  • Property svn:keywords set to Date Rev Author Id
File size: 30.9 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 8695 2009-06-15 00:11:06Z 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
797uint64_t
798tr_date( void )
799{
800    struct timeval tv;
801
802    gettimeofday( &tv, NULL );
803    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
804}
805
806void
807tr_wait( uint64_t delay_milliseconds )
808{
809#ifdef WIN32
810    Sleep( (DWORD)delay_milliseconds );
811#else
812    usleep( 1000 * delay_milliseconds );
813#endif
814}
815
816/***
817****
818***/
819
820int
821tr_snprintf( char *       buf,
822             size_t       buflen,
823             const char * fmt,
824             ... )
825{
826    int     len;
827    va_list args;
828
829    va_start( args, fmt );
830    len = evutil_vsnprintf( buf, buflen, fmt, args );
831    va_end( args );
832    return len;
833}
834
835/*
836 * Copy src to string dst of size siz.  At most siz-1 characters
837 * will be copied.  Always NUL terminates (unless siz == 0).
838 * Returns strlen(src); if retval >= siz, truncation occurred.
839 */
840size_t
841tr_strlcpy( char *       dst,
842            const void * src,
843            size_t       siz )
844{
845#ifdef HAVE_STRLCPY
846    return strlcpy( dst, src, siz );
847#else
848    char *      d = dst;
849    const char *s = src;
850    size_t      n = siz;
851
852    assert( s );
853    assert( d );
854
855    /* Copy as many bytes as will fit */
856    if( n != 0 )
857    {
858        while( --n != 0 )
859        {
860            if( ( *d++ = *s++ ) == '\0' )
861                break;
862        }
863    }
864
865    /* Not enough room in dst, add NUL and traverse rest of src */
866    if( n == 0 )
867    {
868        if( siz != 0 )
869            *d = '\0'; /* NUL-terminate dst */
870        while( *s++ )
871            ;
872    }
873
874    return s - (char*)src - 1;  /* count does not include NUL */
875#endif
876}
877
878/***
879****
880***/
881
882double
883tr_getRatio( double numerator,
884             double denominator )
885{
886    double ratio;
887
888    if( denominator )
889        ratio = numerator / denominator;
890    else if( numerator )
891        ratio = TR_RATIO_INF;
892    else
893        ratio = TR_RATIO_NA;
894
895    return ratio;
896}
897
898void
899tr_sha1_to_hex( char *          out,
900                const uint8_t * sha1 )
901{
902    static const char hex[] = "0123456789abcdef";
903    int               i;
904
905    for( i = 0; i < 20; i++ )
906    {
907        unsigned int val = *sha1++;
908        *out++ = hex[val >> 4];
909        *out++ = hex[val & 0xf];
910    }
911    *out = '\0';
912}
913
914/***
915****
916***/
917
918int
919tr_httpIsValidURL( const char * url )
920{
921    const char *        c;
922    static const char * rfc2396_valid_chars =
923        "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
924        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
925        "0123456789"                 /* digit */
926        "-_.!~*'()"                  /* mark */
927        ";/?:@&=+$,"                 /* reserved */
928        "<>#%<\""                    /* delims */
929        "{}|\\^[]`";                 /* unwise */
930
931    if( url == NULL )
932        return FALSE;
933
934    for( c = url; c && *c; ++c )
935        if( !strchr( rfc2396_valid_chars, *c ) )
936            return FALSE;
937
938    return !tr_httpParseURL( url, -1, NULL, NULL, NULL );
939}
940
941int
942tr_httpParseURL( const char * url_in,
943                 int          len,
944                 char **      setme_host,
945                 int *        setme_port,
946                 char **      setme_path )
947{
948    int          err;
949    int          port = 0;
950    int          n;
951    char *       tmp;
952    char *       pch;
953    const char * protocol = NULL;
954    const char * host = NULL;
955    const char * path = NULL;
956
957    tmp = tr_strndup( url_in, len );
958    if( ( pch = strstr( tmp, "://" ) ) )
959    {
960        *pch = '\0';
961        protocol = tmp;
962        pch += 3;
963/*fprintf( stderr, "protocol is [%s]... what's left is [%s]\n", protocol, pch
964  );*/
965        if( ( n = strcspn( pch, ":/" ) ) )
966        {
967            const int havePort = pch[n] == ':';
968            host = pch;
969            pch += n;
970            *pch++ = '\0';
971/*fprintf( stderr, "host is [%s]... what's left is [%s]\n", host, pch );*/
972            if( havePort )
973            {
974                char * end;
975                port = strtol( pch, &end, 10 );
976                pch = end;
977/*fprintf( stderr, "port is [%d]... what's left is [%s]\n", port, pch );*/
978            }
979            path = pch;
980/*fprintf( stderr, "path is [%s]\n", path );*/
981        }
982    }
983
984    err = !host || !path || !protocol
985          || ( strcmp( protocol, "http" ) && strcmp( protocol, "https" ) );
986
987    if( !err && !port )
988    {
989        if( !strcmp( protocol, "http" ) ) port = 80;
990        if( !strcmp( protocol, "https" ) ) port = 443;
991    }
992
993    if( !err )
994    {
995        if( setme_host ){ ( (char*)host )[-3] = ':'; *setme_host =
996                              tr_strdup( protocol ); }
997        if( setme_path ){ ( (char*)path )[-1] = '/'; *setme_path =
998                              tr_strdup( path - 1 ); }
999        if( setme_port ) *setme_port = port;
1000    }
1001
1002
1003    tr_free( tmp );
1004    return err;
1005}
1006
1007#include <string.h>
1008#include <openssl/sha.h>
1009#include <openssl/hmac.h>
1010#include <openssl/evp.h>
1011#include <openssl/bio.h>
1012#include <openssl/buffer.h>
1013
1014char *
1015tr_base64_encode( const void * input,
1016                  int          length,
1017                  int *        setme_len )
1018{
1019    char *    ret;
1020    BIO *     b64;
1021    BIO *     bmem;
1022    BUF_MEM * bptr;
1023
1024    if( length < 1 )
1025        length = strlen( input );
1026
1027    bmem = BIO_new( BIO_s_mem( ) );
1028    b64 = BIO_new( BIO_f_base64( ) );
1029    b64 = BIO_push( b64, bmem );
1030    BIO_write( b64, input, length );
1031    (void) BIO_flush( b64 );
1032    BIO_get_mem_ptr( b64, &bptr );
1033    ret = tr_strndup( bptr->data, bptr->length );
1034    if( setme_len )
1035        *setme_len = bptr->length;
1036
1037    BIO_free_all( b64 );
1038    return ret;
1039}
1040
1041char *
1042tr_base64_decode( const void * input,
1043                  int          length,
1044                  int *        setme_len )
1045{
1046    char * ret;
1047    BIO *  b64;
1048    BIO *  bmem;
1049    int    retlen;
1050
1051    if( length < 1 )
1052        length = strlen( input );
1053
1054    ret = tr_new0( char, length );
1055    b64 = BIO_new( BIO_f_base64( ) );
1056    bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1057    bmem = BIO_push( b64, bmem );
1058    retlen = BIO_read( bmem, ret, length );
1059    if( !retlen )
1060    {
1061        /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
1062        BIO_free_all( bmem );
1063        b64 = BIO_new( BIO_f_base64( ) );
1064        BIO_set_flags( b64, BIO_FLAGS_BASE64_NO_NL );
1065        bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1066        bmem = BIO_push( b64, bmem );
1067        retlen = BIO_read( bmem, ret, length );
1068    }
1069
1070    if( setme_len )
1071        *setme_len = retlen;
1072
1073    BIO_free_all( bmem );
1074    return ret;
1075}
1076
1077int
1078tr_ptr2int( void* v )
1079{
1080    return (intptr_t)v;
1081}
1082
1083void*
1084tr_int2ptr( int i )
1085{
1086    return (void*)(intptr_t)i;
1087}
1088
1089/***
1090****
1091***/
1092
1093int
1094tr_lowerBound( const void * key,
1095               const void * base,
1096               size_t       nmemb,
1097               size_t       size,
1098               int       (* compar)(const void* key, const void* arrayMember),
1099               tr_bool    * exact_match )
1100{
1101    size_t first = 0;
1102    const char * cbase = base;
1103
1104    while( nmemb )
1105    {
1106        const size_t half = nmemb / 2;
1107        const size_t middle = first + half;
1108        const int c = compar( key, cbase + size*middle );
1109
1110        if( c < 0 )
1111        {
1112            first = middle + 1;
1113            nmemb = nmemb - half - 1;
1114        }
1115        else if( !c )
1116        {
1117            if( exact_match )
1118                *exact_match = TRUE;
1119            return middle;
1120        }
1121        else
1122        {
1123            nmemb = half;
1124        }
1125    }
1126
1127    if( exact_match )
1128        *exact_match = FALSE;
1129
1130    return first;
1131}
1132
1133/***
1134****
1135***/
1136
1137char*
1138tr_utf8clean( const char * str, int max_len, tr_bool * err )
1139{
1140    char * ret;
1141    const char * end;
1142
1143    if( max_len < 0 )
1144        max_len = (int) strlen( str );
1145
1146    if( err != NULL )
1147        *err = FALSE;
1148
1149    if( tr_utf8_validate( str, max_len, &end  ) )
1150    {
1151        ret = tr_strndup( str, max_len );
1152    }
1153    else
1154    {
1155        const char zero = '\0';
1156        struct evbuffer * buf = evbuffer_new( );
1157
1158        while( !tr_utf8_validate ( str, max_len, &end ) )
1159        {
1160            const int good_len = end - str;
1161
1162            evbuffer_add( buf, str, good_len );
1163            max_len -= ( good_len + 1 );
1164            str += ( good_len + 1 );
1165            evbuffer_add( buf, "?", 1 );
1166
1167            if( err != NULL )
1168                *err = TRUE;
1169        }
1170
1171        evbuffer_add( buf, str, max_len );
1172        evbuffer_add( buf, &zero, 1 );
1173        ret = tr_memdup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
1174        evbuffer_free( buf );
1175    }
1176
1177    assert( tr_utf8_validate( ret, -1, NULL ) );
1178    return ret;
1179}
1180
1181/***
1182****
1183***/
1184
1185struct number_range
1186{
1187    int low;
1188    int high;
1189};
1190
1191/**
1192 * This should be a single number (ex. "6") or a range (ex. "6-9").
1193 * Anything else is an error and will return failure.
1194 */
1195static tr_bool
1196parseNumberSection( const char * str, int len, struct number_range * setme )
1197{
1198    long a, b;
1199    tr_bool success;
1200    char * end;
1201    const int error = errno;
1202    char * tmp = tr_strndup( str, len );
1203
1204    errno = 0;
1205    a = b = strtol( tmp, &end, 10 );
1206    if( errno || ( end == tmp ) ) {
1207        success = FALSE;
1208    } else if( *end != '-' ) {
1209        b = a;
1210        success = TRUE;
1211    } else {
1212        const char * pch = end + 1;
1213        b = strtol( pch, &end, 10 );
1214        if( errno || ( pch == end ) )
1215            success = FALSE;
1216        else if( *end ) /* trailing data */
1217            success = FALSE;
1218        else
1219            success = TRUE;
1220    }
1221    tr_free( tmp );
1222
1223    setme->low = MIN( a, b );
1224    setme->high = MAX( a, b );
1225
1226    errno = error;
1227    return success;
1228}
1229
1230static int
1231compareInt( const void * va, const void * vb )
1232{
1233    const int a = *(const int *)va;
1234    const int b = *(const int *)vb;
1235    return a - b;
1236}
1237
1238/**
1239 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1240 * array of setmeCount ints of all the values in the array.
1241 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1242 * It's the caller's responsibility to call tr_free() on the returned array.
1243 * If a fragment of the string can't be parsed, NULL is returned.
1244 */
1245int*
1246tr_parseNumberRange( const char * str_in, int len, int * setmeCount )
1247{
1248    int n = 0;
1249    int * uniq = NULL;
1250    char * str = tr_strndup( str_in, len );
1251    const char * walk;
1252    tr_list * ranges = NULL;
1253    tr_bool success = TRUE;
1254
1255    walk = str;
1256    while( walk && *walk && success ) {
1257        struct number_range range;
1258        const char * pch = strchr( walk, ',' );
1259        if( pch ) {
1260            success = parseNumberSection( walk, pch-walk, &range );
1261            walk = pch + 1;
1262        } else {
1263            success = parseNumberSection( walk, strlen( walk ), &range );
1264            walk += strlen( walk );
1265        }
1266        if( success )
1267            tr_list_append( &ranges, tr_memdup( &range, sizeof( struct number_range ) ) );
1268    }
1269
1270    if( !success )
1271    {
1272        *setmeCount = 0;
1273        uniq = NULL;
1274    }
1275    else
1276    {
1277        int i;
1278        int n2;
1279        tr_list * l;
1280        int * sorted = NULL;
1281
1282        /* build a sorted number array */
1283        n = n2 = 0;
1284        for( l=ranges; l!=NULL; l=l->next ) {
1285            const struct number_range * r = l->data;
1286            n += r->high + 1 - r->low;
1287        }
1288        sorted = tr_new( int, n );
1289        for( l=ranges; l!=NULL; l=l->next ) {
1290            const struct number_range * r = l->data;
1291            int i;
1292            for( i=r->low; i<=r->high; ++i )
1293                sorted[n2++] = i;
1294        }
1295        qsort( sorted, n, sizeof( int ), compareInt );
1296        assert( n == n2 );
1297
1298        /* remove duplicates */
1299        uniq = tr_new( int, n );
1300        for( i=n=0; i<n2; ++i )
1301            if( !n || uniq[n-1] != sorted[i] )
1302                uniq[n++] = sorted[i];
1303
1304        tr_free( sorted );
1305    }
1306
1307    /* cleanup */
1308    tr_list_free( &ranges, tr_free );
1309    tr_free( str );
1310
1311    /* return the result */
1312    *setmeCount = n;
1313    return uniq;
1314}
1315
1316/***
1317****
1318***/
1319
1320static void
1321printf_double_without_rounding( char * buf, int buflen, double d, int places )
1322{
1323    char * pch;
1324    char tmp[128];
1325    int len;
1326    tr_snprintf( tmp, sizeof( tmp ), "%'.64f", d );
1327    pch = tmp;
1328    while( isdigit( *pch ) ) ++pch; /* walk to the decimal point */
1329    ++pch; /* walk over the decimal point */
1330    pch += places;
1331    len = MIN( buflen - 1, pch - tmp );
1332    memcpy( buf, tmp, len );
1333    buf[len] = '\0';
1334}
1335
1336char*
1337tr_strratio( char * buf, size_t buflen, double ratio, const char * infinity )
1338{
1339    if( (int)ratio == TR_RATIO_NA )
1340        tr_strlcpy( buf, _( "None" ), buflen );
1341    else if( (int)ratio == TR_RATIO_INF )
1342        tr_strlcpy( buf, infinity, buflen );
1343    else if( ratio < 10.0 )
1344        printf_double_without_rounding( buf, buflen, ratio, 2 );
1345    else if( ratio < 100.0 )
1346        printf_double_without_rounding( buf, buflen, ratio, 1 );
1347    else
1348        tr_snprintf( buf, buflen, "%'.0f", ratio );
1349    return buf;
1350}
1351
1352/***
1353****
1354***/
1355
1356int
1357tr_moveFile( const char * oldpath, const char * newpath )
1358{
1359    int in;
1360    int out;
1361    char * buf;
1362    struct stat st;
1363    size_t bytesLeft;
1364    size_t buflen;
1365
1366    /* make sure the old file exists */
1367    if( stat( oldpath, &st ) ) {
1368        const int err = errno;
1369        errno = err;
1370        return -1;
1371    }
1372    if( !S_ISREG( st.st_mode ) ) {
1373        errno = ENOENT;
1374        return -1;
1375    }
1376    bytesLeft = st.st_size;
1377
1378    /* make sure the target directory exists */
1379    {
1380        char * newdir = tr_dirname( newpath );
1381        int i = tr_mkdirp( newdir, 0777 );
1382        tr_free( newdir );
1383        if( i )
1384            return i;
1385    }
1386
1387    /* they  might be on the same filesystem... */
1388    if( !rename( oldpath, newpath ) )
1389        return 0;
1390
1391    /* copy the file */
1392    in = tr_open_file_for_scanning( oldpath );
1393    tr_preallocate_file( newpath, bytesLeft );
1394    out = tr_open_file_for_writing( newpath );
1395    buflen = stat( newpath, &st ) ? 4096 : st.st_blksize;
1396    buf = tr_new( char, buflen );
1397    while( bytesLeft > 0 )
1398    {
1399        ssize_t bytesWritten;
1400        const size_t bytesThisPass = MIN( bytesLeft, buflen );
1401        const int numRead = read( in, buf, bytesThisPass );
1402        if( numRead < 0 )
1403            break;
1404        bytesWritten = write( out, buf, numRead );
1405        if( bytesWritten < 0 )
1406            break;
1407        bytesLeft -= bytesWritten;
1408    }
1409
1410    /* cleanup */
1411    tr_free( buf );
1412    tr_close_file( out );
1413    tr_close_file( in );
1414    if( bytesLeft != 0 )
1415        return -1;
1416
1417    unlink( oldpath );
1418    return 0;
1419}
Note: See TracBrowser for help on using the repository browser.