source: trunk/libtransmission/utils.c @ 9382

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

(trunk libT) revert r9345, because all our .torrent and .resume files' hex letters are in lowercase

  • 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 9382 2009-10-22 18:48:26Z 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 <math.h> /* pow */
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h> /* strerror, memset, memmem */
25
26#include <libgen.h> /* basename */
27#include <sys/time.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h> /* usleep, stat, getcwd */
31
32#include "event.h"
33
34#ifdef WIN32
35 #include <direct.h> /* _getcwd */
36 #include <windows.h> /* Sleep */
37#endif
38
39#include "transmission.h"
40#include "fdlimit.h"
41#include "ConvertUTF.h"
42#include "list.h"
43#include "utils.h"
44#include "platform.h"
45#include "version.h"
46
47static tr_lock *      messageLock = NULL;
48static int            messageLevel = 0;
49static tr_bool        messageQueuing = FALSE;
50static tr_msg_list *  messageQueue = NULL;
51static tr_msg_list ** messageQueueTail = &messageQueue;
52
53#ifndef WIN32
54    /* make null versions of these win32 functions */
55    static int IsDebuggerPresent( void ) { return FALSE; }
56    static void OutputDebugString( const void * unused UNUSED ) { }
57#endif
58
59static void
60tr_msgInit( void )
61{
62    static tr_bool initialized = FALSE;
63
64    if( !initialized )
65    {
66        char * env = getenv( "TR_DEBUG" );
67        messageLevel = ( env ? atoi( env ) : 0 ) + 1;
68        messageLevel = MAX( 1, messageLevel );
69
70        messageLock = tr_lockNew( );
71
72        initialized = TRUE;
73    }
74}
75
76FILE*
77tr_getLog( void )
78{
79    static int    initialized = FALSE;
80    static FILE * file = NULL;
81
82    if( !initialized )
83    {
84        const char * str = getenv( "TR_DEBUG_FD" );
85        int          fd = 0;
86        if( str && *str )
87            fd = atoi( str );
88        switch( fd )
89        {
90            case 1:
91                file = stdout; break;
92
93            case 2:
94                file = stderr; break;
95
96            default:
97                file = NULL; break;
98        }
99        initialized = TRUE;
100    }
101
102    return file;
103}
104
105void
106tr_setMessageLevel( int level )
107{
108    tr_msgInit( );
109    tr_lockLock( messageLock );
110
111    messageLevel = MAX( 0, level );
112
113    tr_lockUnlock( messageLock );
114}
115
116int
117tr_getMessageLevel( void )
118{
119    int ret;
120    tr_msgInit( );
121    tr_lockLock( messageLock );
122
123    ret = messageLevel;
124
125    tr_lockUnlock( messageLock );
126    return ret;
127}
128
129void
130tr_setMessageQueuing( tr_bool enabled )
131{
132    tr_msgInit( );
133    tr_lockLock( messageLock );
134
135    messageQueuing = enabled;
136
137    tr_lockUnlock( messageLock );
138}
139
140tr_bool
141tr_getMessageQueuing( void )
142{
143    int ret;
144    tr_msgInit( );
145    tr_lockLock( messageLock );
146
147    ret = messageQueuing;
148
149    tr_lockUnlock( messageLock );
150    return ret;
151}
152
153tr_msg_list *
154tr_getQueuedMessages( void )
155{
156    tr_msg_list * ret;
157    tr_msgInit( );
158    tr_lockLock( messageLock );
159
160    ret = messageQueue;
161    messageQueue = NULL;
162    messageQueueTail = &messageQueue;
163
164    tr_lockUnlock( messageLock );
165    return ret;
166}
167
168void
169tr_freeMessageList( tr_msg_list * list )
170{
171    tr_msg_list * next;
172
173    while( NULL != list )
174    {
175        next = list->next;
176        free( list->message );
177        free( list->name );
178        free( list );
179        list = next;
180    }
181}
182
183/**
184***
185**/
186
187struct tm *
188tr_localtime_r( const time_t *_clock, struct tm *_result )
189{
190#ifdef HAVE_LOCALTIME_R
191    return localtime_r( _clock, _result );
192#else
193    struct tm *p = localtime( _clock );
194    if( p )
195        *(_result) = *p;
196    return p;
197#endif
198}
199
200char*
201tr_getLogTimeStr( char * buf, int buflen )
202{
203    char           tmp[64];
204    time_t         now;
205    struct tm      now_tm;
206    struct timeval tv;
207    int            milliseconds;
208
209    now = time( NULL );
210    gettimeofday( &tv, NULL );
211
212    tr_localtime_r( &now, &now_tm );
213    strftime( tmp, sizeof( tmp ), "%H:%M:%S", &now_tm );
214    milliseconds = (int)( tv.tv_usec / 1000 );
215    tr_snprintf( buf, buflen, "%s.%03d", tmp, milliseconds );
216
217    return buf;
218}
219
220void
221tr_assertImpl( const char * file, int line, const char * test, const char * fmt, ... )
222{
223    char buf[64];
224    fprintf( stderr, "[%s] Transmission %s Assertion \"%s\" failed at %s:%d.  ",
225                     tr_getLogTimeStr( buf, sizeof( buf ) ),
226                      LONG_VERSION_STRING, test, file, line );
227    if( fmt && *fmt ) {
228        va_list args;
229        fputc( '(', stderr );
230        va_start( args, fmt );
231        vfprintf( stderr, fmt, args );
232        va_end( args );
233        fputs( ")  ", stderr );
234    }
235    fputs( "Please report this bug at <http://trac.transmissionbt.com/newticket>; Thank you.\n", stderr );
236    abort( );
237}
238
239
240tr_bool
241tr_deepLoggingIsActive( void )
242{
243    static int8_t deepLoggingIsActive = -1;
244
245    if( deepLoggingIsActive < 0 )
246        deepLoggingIsActive = IsDebuggerPresent() || (tr_getLog()!=NULL);
247
248    return deepLoggingIsActive != 0;
249}
250
251void
252tr_deepLog( const char  * file,
253            int           line,
254            const char  * name,
255            const char  * fmt,
256            ... )
257{
258    FILE * fp = tr_getLog( );
259    if( fp || IsDebuggerPresent( ) )
260    {
261        va_list           args;
262        char              timestr[64];
263        struct evbuffer * buf = evbuffer_new( );
264        char *            base = tr_basename( file );
265
266        evbuffer_add_printf( buf, "[%s] ",
267                            tr_getLogTimeStr( timestr, sizeof( timestr ) ) );
268        if( name )
269            evbuffer_add_printf( buf, "%s ", name );
270        va_start( args, fmt );
271        evbuffer_add_vprintf( buf, fmt, args );
272        va_end( args );
273        evbuffer_add_printf( buf, " (%s:%d)\n", base, line );
274        /* FIXME(libevent2) ifdef this out for nonwindows platforms */
275        OutputDebugString( EVBUFFER_DATA( buf ) );
276        if(fp) /* FIXME(libevent2) tr_getLog() should return an fd, then use evbuffer_write() here ) */
277            (void) fwrite( EVBUFFER_DATA( buf ), 1, EVBUFFER_LENGTH( buf ), fp );
278
279        tr_free( base );
280        evbuffer_free( buf );
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    const char * in = str;
426    static size_t bufsize = 0;
427    static char * buf = NULL;
428    const size_t  len = strlen( str );
429    char *        out;
430
431    if( bufsize < len )
432    {
433        bufsize = len * 2;
434        buf = tr_renew( char, buf, bufsize );
435    }
436
437    for( out = buf; *str; ++str )
438    {
439        *out++ = *str;
440        if( ( *str == '%' ) && isdigit( str[1] ) )
441        {
442            const char * tmp = str + 1;
443            while( isdigit( *tmp ) )
444                ++tmp;
445
446            if( *tmp == '$' )
447                str = tmp;
448        }
449    }
450    *out = '\0';
451
452    return strcmp( buf, in ) ? buf : in;
453}
454
455#endif
456
457/**
458***
459**/
460
461tr_bool
462tr_isTimeval( const struct timeval * tv )
463{
464    return tv && ( tv->tv_sec >= 0 )
465              && ( tv->tv_usec >= 0 )
466              && ( tv->tv_usec < 1000000 );
467}
468
469void
470tr_timevalMsec( uint64_t milliseconds, struct timeval * setme )
471{
472    const uint64_t microseconds = milliseconds * 1000;
473    assert( setme != NULL );
474    setme->tv_sec  = microseconds / 1000000;
475    setme->tv_usec = microseconds % 1000000;
476    assert( tr_isTimeval( setme ) );
477}
478
479void
480tr_timevalSet( struct timeval * setme, int seconds, int microseconds )
481{
482    setme->tv_sec = seconds;
483    setme->tv_usec = microseconds;
484    assert( tr_isTimeval( setme ) );
485}
486
487void
488tr_timerAdd( struct event * timer, int seconds, int milliseconds )
489{
490    struct timeval tv;
491    tr_timevalSet( &tv, seconds, milliseconds );
492    event_add( timer, &tv );
493}
494
495/**
496***
497**/
498
499uint8_t *
500tr_loadFile( const char * path,
501             size_t *     size )
502{
503    uint8_t * buf;
504    struct stat  sb;
505    int fd;
506    ssize_t n;
507    const char * err_fmt = _( "Couldn't read \"%1$s\": %2$s" );
508
509    /* try to stat the file */
510    errno = 0;
511    if( stat( path, &sb ) )
512    {
513        const int err = errno;
514        tr_dbg( err_fmt, path, tr_strerror( errno ) );
515        errno = err;
516        return NULL;
517    }
518
519    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
520    {
521        tr_err( err_fmt, path, _( "Not a regular file" ) );
522        errno = EISDIR;
523        return NULL;
524    }
525
526    /* Load the torrent file into our buffer */
527    fd = tr_open_file_for_scanning( path );
528    if( fd < 0 )
529    {
530        const int err = errno;
531        tr_err( err_fmt, path, tr_strerror( errno ) );
532        errno = err;
533        return NULL;
534    }
535    buf = malloc( sb.st_size + 1 );
536    if( !buf )
537    {
538        const int err = errno;
539        tr_err( err_fmt, path, _( "Memory allocation failed" ) );
540        tr_close_file( fd );
541        errno = err;
542        return NULL;
543    }
544    n = read( fd, buf, sb.st_size );
545    if( n == -1 )
546    {
547        const int err = errno;
548        tr_err( err_fmt, path, tr_strerror( errno ) );
549        tr_close_file( fd );
550        free( buf );
551        errno = err;
552        return NULL;
553    }
554
555    tr_close_file( fd );
556    buf[ sb.st_size ] = '\0';
557    *size = sb.st_size;
558    return buf;
559}
560
561char*
562tr_basename( const char * path )
563{
564    char * tmp = tr_strdup( path );
565    char * ret = tr_strdup( basename( tmp ) );
566    tr_free( tmp );
567    return ret;
568}
569
570char*
571tr_dirname( const char * path )
572{
573    char * tmp = tr_strdup( path );
574    char * ret = tr_strdup( dirname( tmp ) );
575    tr_free( tmp );
576    return ret;
577}
578
579int
580tr_mkdir( const char * path,
581          int permissions
582#ifdef WIN32
583                       UNUSED
584#endif
585        )
586{
587#ifdef WIN32
588    if( path && isalpha( path[0] ) && path[1] == ':' && !path[2] )
589        return 0;
590    return mkdir( path );
591#else
592    return mkdir( path, permissions );
593#endif
594}
595
596int
597tr_mkdirp( const char * path_in,
598           int          permissions )
599{
600    char *      path = tr_strdup( path_in );
601    char *      p, * pp;
602    struct stat sb;
603    int         done;
604
605    /* walk past the root */
606    p = path;
607    while( *p == TR_PATH_DELIMITER )
608        ++p;
609
610    pp = p;
611    done = 0;
612    while( ( p =
613                strchr( pp, TR_PATH_DELIMITER ) ) || ( p = strchr( pp, '\0' ) ) )
614    {
615        if( !*p )
616            done = 1;
617        else
618            *p = '\0';
619
620        if( stat( path, &sb ) )
621        {
622            /* Folder doesn't exist yet */
623            if( tr_mkdir( path, permissions ) )
624            {
625                const int err = errno;
626                tr_err( _(
627                           "Couldn't create \"%1$s\": %2$s" ), path,
628                       tr_strerror( err ) );
629                tr_free( path );
630                errno = err;
631                return -1;
632            }
633        }
634        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
635        {
636            /* Node exists but isn't a folder */
637            char * buf = tr_strdup_printf( _( "File \"%s\" is in the way" ), path );
638            tr_err( _( "Couldn't create \"%1$s\": %2$s" ), path_in, buf );
639            tr_free( buf );
640            tr_free( path );
641            errno = ENOTDIR;
642            return -1;
643        }
644
645        if( done )
646            break;
647
648        *p = TR_PATH_DELIMITER;
649        p++;
650        pp = p;
651    }
652
653    tr_free( path );
654    return 0;
655}
656
657char*
658tr_buildPath( const char *first_element, ... )
659{
660    size_t bufLen = 0;
661    const char * element;
662    char * buf;
663    char * pch;
664    va_list vl;
665
666    /* pass 1: allocate enough space for the string */
667    va_start( vl, first_element );
668    element = first_element;
669    while( element ) {
670        bufLen += strlen( element ) + 1;
671        element = (const char*) va_arg( vl, const char* );
672    }
673    pch = buf = tr_new( char, bufLen );
674    va_end( vl );
675
676    /* pass 2: build the string piece by piece */
677    va_start( vl, first_element );
678    element = first_element;
679    while( element ) {
680        const size_t elementLen = strlen( element );
681        memcpy( pch, element, elementLen );
682        pch += elementLen;
683        *pch++ = TR_PATH_DELIMITER;
684        element = (const char*) va_arg( vl, const char* );
685    }
686    va_end( vl );
687
688    /* terminate the string.  if nonempty, eat the unwanted trailing slash */
689    if( pch != buf )
690        --pch;
691    *pch++ = '\0';
692
693    /* sanity checks & return */
694    assert( pch - buf == (off_t)bufLen );
695    return buf;
696}
697
698/****
699*****
700****/
701
702char*
703tr_strndup( const void * in, int len )
704{
705    char * out = NULL;
706
707    if( len < 0 )
708    {
709        out = tr_strdup( in );
710    }
711    else if( in )
712    {
713        out = tr_malloc( len + 1 );
714        memcpy( out, in, len );
715        out[len] = '\0';
716    }
717
718    return out;
719}
720
721const char*
722tr_memmem( const char * haystack, size_t haystacklen,
723           const char * needle, size_t needlelen )
724{
725#ifdef HAVE_MEMMEM
726    return memmem( haystack, haystacklen, needle, needlelen );
727#else
728    size_t i;
729    if( !needlelen )
730        return haystack;
731    if( needlelen > haystacklen || !haystack || !needle )
732        return NULL;
733    for( i=0; i<=haystacklen-needlelen; ++i )
734        if( !memcmp( haystack+i, needle, needlelen ) )
735            return haystack+i;
736    return NULL;
737#endif
738}
739
740char*
741tr_strdup_printf( const char * fmt, ... )
742{
743    va_list ap;
744    char * ret;
745    size_t len;
746    char statbuf[2048];
747
748    va_start( ap, fmt );
749    len = evutil_vsnprintf( statbuf, sizeof( statbuf ), fmt, ap );
750    va_end( ap );
751    if( len < sizeof( statbuf ) )
752        ret = tr_strndup( statbuf, len );
753    else {
754        ret = tr_new( char, len + 1 );
755        va_start( ap, fmt );
756        evutil_vsnprintf( ret, len + 1, fmt, ap );
757        va_end( ap );
758    }
759
760    return ret;
761}
762
763const char*
764tr_strerror( int i )
765{
766    const char * ret = strerror( i );
767
768    if( ret == NULL )
769        ret = "Unknown Error";
770    return ret;
771}
772
773/****
774*****
775****/
776
777char*
778tr_strstrip( char * str )
779{
780    if( str != NULL )
781    {
782        size_t pos;
783        size_t len = strlen( str );
784
785        while( len && isspace( str[len - 1] ) )
786            --len;
787
788        str[len] = '\0';
789
790        for( pos = 0; pos < len && isspace( str[pos] ); )
791            ++pos;
792
793        len -= pos;
794        memmove( str, str + pos, len );
795        str[len] = '\0';
796    }
797
798    return str;
799}
800
801/****
802*****
803****/
804
805uint64_t
806tr_date( void )
807{
808    struct timeval tv;
809
810    gettimeofday( &tv, NULL );
811    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
812}
813
814void
815tr_wait( uint64_t delay_milliseconds )
816{
817#ifdef WIN32
818    Sleep( (DWORD)delay_milliseconds );
819#else
820    usleep( 1000 * delay_milliseconds );
821#endif
822}
823
824/***
825****
826***/
827
828int
829tr_snprintf( char *       buf,
830             size_t       buflen,
831             const char * fmt,
832             ... )
833{
834    int     len;
835    va_list args;
836
837    va_start( args, fmt );
838    len = evutil_vsnprintf( buf, buflen, fmt, args );
839    va_end( args );
840    return len;
841}
842
843/*
844 * Copy src to string dst of size siz.  At most siz-1 characters
845 * will be copied.  Always NUL terminates (unless siz == 0).
846 * Returns strlen(src); if retval >= siz, truncation occurred.
847 */
848size_t
849tr_strlcpy( char *       dst,
850            const void * src,
851            size_t       siz )
852{
853#ifdef HAVE_STRLCPY
854    return strlcpy( dst, src, siz );
855#else
856    char *      d = dst;
857    const char *s = src;
858    size_t      n = siz;
859
860    assert( s );
861    assert( d );
862
863    /* Copy as many bytes as will fit */
864    if( n != 0 )
865    {
866        while( --n != 0 )
867        {
868            if( ( *d++ = *s++ ) == '\0' )
869                break;
870        }
871    }
872
873    /* Not enough room in dst, add NUL and traverse rest of src */
874    if( n == 0 )
875    {
876        if( siz != 0 )
877            *d = '\0'; /* NUL-terminate dst */
878        while( *s++ )
879            ;
880    }
881
882    return s - (char*)src - 1;  /* count does not include NUL */
883#endif
884}
885
886/***
887****
888***/
889
890double
891tr_getRatio( double numerator,
892             double denominator )
893{
894    double ratio;
895
896    if( denominator )
897        ratio = numerator / denominator;
898    else if( numerator )
899        ratio = TR_RATIO_INF;
900    else
901        ratio = TR_RATIO_NA;
902
903    return ratio;
904}
905
906void
907tr_sha1_to_hex( char *          out,
908                const uint8_t * sha1 )
909{
910    static const char hex[] = "0123456789abcdef";
911    int               i;
912
913    for( i = 0; i < 20; i++ )
914    {
915        unsigned int val = *sha1++;
916        *out++ = hex[val >> 4];
917        *out++ = hex[val & 0xf];
918    }
919    *out = '\0';
920}
921
922/***
923****
924***/
925
926tr_bool
927tr_httpIsValidURL( const char * url )
928{
929    const char * c;
930    static const char * rfc2396_valid_chars =
931        "abcdefghijklmnopqrstuvwxyz" /* lowalpha */
932        "ABCDEFGHIJKLMNOPQRSTUVWXYZ" /* upalpha */
933        "0123456789"                 /* digit */
934        "-_.!~*'()"                  /* mark */
935        ";/?:@&=+$,"                 /* reserved */
936        "<>#%<\""                    /* delims */
937        "{}|\\^[]`";                 /* unwise */
938
939    if( url == NULL )
940        return FALSE;
941
942    for( c = url; c && *c; ++c )
943        if( !strchr( rfc2396_valid_chars, *c ) )
944            return FALSE;
945
946    return tr_httpParseURL( url, -1, NULL, NULL, NULL ) == 0;
947}
948
949int
950tr_httpParseURL( const char * url_in,
951                 int          len,
952                 char **      setme_host,
953                 int *        setme_port,
954                 char **      setme_path )
955{
956    int          err;
957    int          port = 0;
958    int          n;
959    char *       tmp;
960    char *       pch;
961    const char * protocol = NULL;
962    const char * host = NULL;
963    const char * path = NULL;
964
965    tmp = tr_strndup( url_in, len );
966    if( ( pch = strstr( tmp, "://" ) ) )
967    {
968        *pch = '\0';
969        protocol = tmp;
970        pch += 3;
971/*fprintf( stderr, "protocol is [%s]... what's left is [%s]\n", protocol, pch
972  );*/
973        if( ( n = strcspn( pch, ":/" ) ) )
974        {
975            const int havePort = pch[n] == ':';
976            host = pch;
977            pch += n;
978            *pch++ = '\0';
979/*fprintf( stderr, "host is [%s]... what's left is [%s]\n", host, pch );*/
980            if( havePort )
981            {
982                char * end;
983                port = strtol( pch, &end, 10 );
984                pch = end;
985/*fprintf( stderr, "port is [%d]... what's left is [%s]\n", port, pch );*/
986            }
987            path = pch;
988/*fprintf( stderr, "path is [%s]\n", path );*/
989        }
990    }
991
992    err = !host || !path || !protocol
993          || ( strcmp( protocol, "http" ) && strcmp( protocol, "https" ) );
994
995    if( !err && !port )
996    {
997        if( !strcmp( protocol, "http" ) ) port = 80;
998        if( !strcmp( protocol, "https" ) ) port = 443;
999    }
1000
1001    if( !err )
1002    {
1003        if( setme_host ){ ( (char*)host )[-3] = ':'; *setme_host =
1004                              tr_strdup( protocol ); }
1005        if( setme_path ){ ( (char*)path )[-1] = '/'; *setme_path =
1006                              tr_strdup( path - 1 ); }
1007        if( setme_port ) *setme_port = port;
1008    }
1009
1010
1011    tr_free( tmp );
1012    return err;
1013}
1014
1015#include <string.h>
1016#include <openssl/sha.h>
1017#include <openssl/hmac.h>
1018#include <openssl/evp.h>
1019#include <openssl/bio.h>
1020#include <openssl/buffer.h>
1021
1022char *
1023tr_base64_encode( const void * input,
1024                  int          length,
1025                  int *        setme_len )
1026{
1027    char *    ret;
1028    BIO *     b64;
1029    BIO *     bmem;
1030    BUF_MEM * bptr;
1031
1032    if( length < 1 )
1033        length = strlen( input );
1034
1035    bmem = BIO_new( BIO_s_mem( ) );
1036    b64 = BIO_new( BIO_f_base64( ) );
1037    b64 = BIO_push( b64, bmem );
1038    BIO_write( b64, input, length );
1039    (void) BIO_flush( b64 );
1040    BIO_get_mem_ptr( b64, &bptr );
1041    ret = tr_strndup( bptr->data, bptr->length );
1042    if( setme_len )
1043        *setme_len = bptr->length;
1044
1045    BIO_free_all( b64 );
1046    return ret;
1047}
1048
1049char *
1050tr_base64_decode( const void * input,
1051                  int          length,
1052                  int *        setme_len )
1053{
1054    char * ret;
1055    BIO *  b64;
1056    BIO *  bmem;
1057    int    retlen;
1058
1059    if( length < 1 )
1060        length = strlen( input );
1061
1062    ret = tr_new0( char, length );
1063    b64 = BIO_new( BIO_f_base64( ) );
1064    bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1065    bmem = BIO_push( b64, bmem );
1066    retlen = BIO_read( bmem, ret, length );
1067    if( !retlen )
1068    {
1069        /* try again, but with the BIO_FLAGS_BASE64_NO_NL flag */
1070        BIO_free_all( bmem );
1071        b64 = BIO_new( BIO_f_base64( ) );
1072        BIO_set_flags( b64, BIO_FLAGS_BASE64_NO_NL );
1073        bmem = BIO_new_mem_buf( (unsigned char*)input, length );
1074        bmem = BIO_push( b64, bmem );
1075        retlen = BIO_read( bmem, ret, length );
1076    }
1077
1078    if( setme_len )
1079        *setme_len = retlen;
1080
1081    BIO_free_all( bmem );
1082    return ret;
1083}
1084
1085int
1086tr_ptr2int( void* v )
1087{
1088    return (intptr_t)v;
1089}
1090
1091void*
1092tr_int2ptr( int i )
1093{
1094    return (void*)(intptr_t)i;
1095}
1096
1097/***
1098****
1099***/
1100
1101int
1102tr_lowerBound( const void * key,
1103               const void * base,
1104               size_t       nmemb,
1105               size_t       size,
1106               int       (* compar)(const void* key, const void* arrayMember),
1107               tr_bool    * exact_match )
1108{
1109    size_t first = 0;
1110    const char * cbase = base;
1111
1112    while( nmemb )
1113    {
1114        const size_t half = nmemb / 2;
1115        const size_t middle = first + half;
1116        const int c = compar( key, cbase + size*middle );
1117
1118        if( c < 0 )
1119        {
1120            first = middle + 1;
1121            nmemb = nmemb - half - 1;
1122        }
1123        else if( !c )
1124        {
1125            if( exact_match )
1126                *exact_match = TRUE;
1127            return middle;
1128        }
1129        else
1130        {
1131            nmemb = half;
1132        }
1133    }
1134
1135    if( exact_match )
1136        *exact_match = FALSE;
1137
1138    return first;
1139}
1140
1141/***
1142****
1143***/
1144
1145char*
1146tr_utf8clean( const char * str, int max_len, tr_bool * err )
1147{
1148    char * ret;
1149    const char * end;
1150
1151    if( max_len < 0 )
1152        max_len = (int) strlen( str );
1153
1154    if( err != NULL )
1155        *err = FALSE;
1156
1157    if( tr_utf8_validate( str, max_len, &end  ) )
1158    {
1159        ret = tr_strndup( str, max_len );
1160    }
1161    else
1162    {
1163        const char zero = '\0';
1164        struct evbuffer * buf = evbuffer_new( );
1165
1166        while( !tr_utf8_validate ( str, max_len, &end ) )
1167        {
1168            const int good_len = end - str;
1169
1170            evbuffer_add( buf, str, good_len );
1171            max_len -= ( good_len + 1 );
1172            str += ( good_len + 1 );
1173            evbuffer_add( buf, "?", 1 );
1174
1175            if( err != NULL )
1176                *err = TRUE;
1177        }
1178
1179        evbuffer_add( buf, str, max_len );
1180        evbuffer_add( buf, &zero, 1 );
1181        ret = tr_memdup( EVBUFFER_DATA( buf ), EVBUFFER_LENGTH( buf ) );
1182        evbuffer_free( buf );
1183    }
1184
1185    assert( tr_utf8_validate( ret, -1, NULL ) );
1186    return ret;
1187}
1188
1189/***
1190****
1191***/
1192
1193struct number_range
1194{
1195    int low;
1196    int high;
1197};
1198
1199/**
1200 * This should be a single number (ex. "6") or a range (ex. "6-9").
1201 * Anything else is an error and will return failure.
1202 */
1203static tr_bool
1204parseNumberSection( const char * str, int len, struct number_range * setme )
1205{
1206    long a, b;
1207    tr_bool success;
1208    char * end;
1209    const int error = errno;
1210    char * tmp = tr_strndup( str, len );
1211
1212    errno = 0;
1213    a = b = strtol( tmp, &end, 10 );
1214    if( errno || ( end == tmp ) ) {
1215        success = FALSE;
1216    } else if( *end != '-' ) {
1217        b = a;
1218        success = TRUE;
1219    } else {
1220        const char * pch = end + 1;
1221        b = strtol( pch, &end, 10 );
1222        if( errno || ( pch == end ) )
1223            success = FALSE;
1224        else if( *end ) /* trailing data */
1225            success = FALSE;
1226        else
1227            success = TRUE;
1228    }
1229    tr_free( tmp );
1230
1231    setme->low = MIN( a, b );
1232    setme->high = MAX( a, b );
1233
1234    errno = error;
1235    return success;
1236}
1237
1238static int
1239compareInt( const void * va, const void * vb )
1240{
1241    const int a = *(const int *)va;
1242    const int b = *(const int *)vb;
1243    return a - b;
1244}
1245
1246/**
1247 * Given a string like "1-4" or "1-4,6,9,14-51", this allocates and returns an
1248 * array of setmeCount ints of all the values in the array.
1249 * For example, "5-8" will return [ 5, 6, 7, 8 ] and setmeCount will be 4.
1250 * It's the caller's responsibility to call tr_free() on the returned array.
1251 * If a fragment of the string can't be parsed, NULL is returned.
1252 */
1253int*
1254tr_parseNumberRange( const char * str_in, int len, int * setmeCount )
1255{
1256    int n = 0;
1257    int * uniq = NULL;
1258    char * str = tr_strndup( str_in, len );
1259    const char * walk;
1260    tr_list * ranges = NULL;
1261    tr_bool success = TRUE;
1262
1263    walk = str;
1264    while( walk && *walk && success ) {
1265        struct number_range range;
1266        const char * pch = strchr( walk, ',' );
1267        if( pch ) {
1268            success = parseNumberSection( walk, pch-walk, &range );
1269            walk = pch + 1;
1270        } else {
1271            success = parseNumberSection( walk, strlen( walk ), &range );
1272            walk += strlen( walk );
1273        }
1274        if( success )
1275            tr_list_append( &ranges, tr_memdup( &range, sizeof( struct number_range ) ) );
1276    }
1277
1278    if( !success )
1279    {
1280        *setmeCount = 0;
1281        uniq = NULL;
1282    }
1283    else
1284    {
1285        int i;
1286        int n2;
1287        tr_list * l;
1288        int * sorted = NULL;
1289
1290        /* build a sorted number array */
1291        n = n2 = 0;
1292        for( l=ranges; l!=NULL; l=l->next ) {
1293            const struct number_range * r = l->data;
1294            n += r->high + 1 - r->low;
1295        }
1296        sorted = tr_new( int, n );
1297        for( l=ranges; l!=NULL; l=l->next ) {
1298            const struct number_range * r = l->data;
1299            int i;
1300            for( i=r->low; i<=r->high; ++i )
1301                sorted[n2++] = i;
1302        }
1303        qsort( sorted, n, sizeof( int ), compareInt );
1304        assert( n == n2 );
1305
1306        /* remove duplicates */
1307        uniq = tr_new( int, n );
1308        for( i=n=0; i<n2; ++i )
1309            if( !n || uniq[n-1] != sorted[i] )
1310                uniq[n++] = sorted[i];
1311
1312        tr_free( sorted );
1313    }
1314
1315    /* cleanup */
1316    tr_list_free( &ranges, tr_free );
1317    tr_free( str );
1318
1319    /* return the result */
1320    *setmeCount = n;
1321    return uniq;
1322}
1323
1324/***
1325****
1326***/
1327
1328double
1329tr_truncd( double x, int decimal_places )
1330{
1331    static const int multiplier[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
1332    const int64_t i = multiplier[decimal_places];
1333    double x2 = (int64_t)(x*i);
1334    return x2 / i;
1335}
1336
1337char*
1338tr_strratio( char * buf, size_t buflen, double ratio, const char * infinity )
1339{
1340    if( (int)ratio == TR_RATIO_NA )
1341        tr_strlcpy( buf, _( "None" ), buflen );
1342    else if( (int)ratio == TR_RATIO_INF )
1343        tr_strlcpy( buf, infinity, buflen );
1344    else if( ratio < 10.0 )
1345        tr_snprintf( buf, buflen, "%.2f", tr_truncd( ratio, 2 ) );
1346    else if( ratio < 100.0 )
1347        tr_snprintf( buf, buflen, "%.1f", tr_truncd( ratio, 1 ) );
1348    else
1349        tr_snprintf( buf, buflen, "%'.0f", ratio );
1350    return buf;
1351}
1352
1353/***
1354****
1355***/
1356
1357int
1358tr_moveFile( const char * oldpath, const char * newpath )
1359{
1360    int in;
1361    int out;
1362    char * buf;
1363    struct stat st;
1364    size_t bytesLeft;
1365    size_t buflen;
1366
1367    /* make sure the old file exists */
1368    if( stat( oldpath, &st ) ) {
1369        const int err = errno;
1370        errno = err;
1371        return -1;
1372    }
1373    if( !S_ISREG( st.st_mode ) ) {
1374        errno = ENOENT;
1375        return -1;
1376    }
1377    bytesLeft = st.st_size;
1378
1379    /* make sure the target directory exists */
1380    {
1381        char * newdir = tr_dirname( newpath );
1382        int i = tr_mkdirp( newdir, 0777 );
1383        tr_free( newdir );
1384        if( i )
1385            return i;
1386    }
1387
1388    /* they  might be on the same filesystem... */
1389    if( !rename( oldpath, newpath ) )
1390        return 0;
1391
1392    /* copy the file */
1393    in = tr_open_file_for_scanning( oldpath );
1394    tr_preallocate_file( newpath, bytesLeft );
1395    out = tr_open_file_for_writing( newpath );
1396    buflen = stat( newpath, &st ) ? 4096 : st.st_blksize;
1397    buf = tr_new( char, buflen );
1398    while( bytesLeft > 0 )
1399    {
1400        ssize_t bytesWritten;
1401        const size_t bytesThisPass = MIN( bytesLeft, buflen );
1402        const int numRead = read( in, buf, bytesThisPass );
1403        if( numRead < 0 )
1404            break;
1405        bytesWritten = write( out, buf, numRead );
1406        if( bytesWritten < 0 )
1407            break;
1408        bytesLeft -= bytesWritten;
1409    }
1410
1411    /* cleanup */
1412    tr_free( buf );
1413    tr_close_file( out );
1414    tr_close_file( in );
1415    if( bytesLeft != 0 )
1416        return -1;
1417
1418    unlink( oldpath );
1419    return 0;
1420}
Note: See TracBrowser for help on using the repository browser.