source: trunk/libtransmission/utils.c @ 5112

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

added tr_sha1_to_hex()

  • Property svn:keywords set to Date Rev Author Id
File size: 19.6 KB
Line 
1/******************************************************************************
2 * $Id: utils.c 5112 2008-02-25 20:21:22Z charles $
3 *
4 * Copyright (c) 2005-2008 Transmission authors and contributors
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 *****************************************************************************/
24
25#include <assert.h>
26#include <errno.h>
27#include <stdarg.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h> /* strerror */
31
32#include <sys/time.h>
33#include <sys/types.h>
34#include <sys/stat.h>
35#include <unistd.h> /* usleep, stat */
36
37#include "event.h"
38
39#ifdef WIN32
40    #include <windows.h> /* for Sleep */
41#elif defined(__BEOS__)
42    #include <kernel/OS.h>
43#endif
44
45#include "transmission.h"
46#include "trcompat.h"
47#include "utils.h"
48#include "platform.h"
49
50static tr_lock      * messageLock = NULL;
51static int            messageLevel = 0;
52static int            messageQueuing = FALSE;
53static tr_msg_list *  messageQueue = NULL;
54static tr_msg_list ** messageQueueTail = &messageQueue;
55
56void tr_msgInit( void )
57{
58    if( !messageLock )
59         messageLock = tr_lockNew( );
60}
61
62FILE*
63tr_getLog( void )
64{
65    static int initialized = FALSE;
66    static FILE * file= NULL;
67
68    if( !initialized )
69    {
70        const char * str = getenv( "TR_DEBUG_FD" );
71        int fd = 0;
72        if( str && *str )
73            fd = atoi( str );
74        switch( fd ) {
75            case 1: file = stdout; break;
76            case 2: file = stderr; break;
77            default: file = NULL; break;
78        }
79        initialized = TRUE;
80    }
81
82    return file;
83}
84
85char*
86tr_getLogTimeStr( char * buf, int buflen )
87{
88    char tmp[64];
89    time_t now;
90    struct tm now_tm;
91    struct timeval tv;
92    int milliseconds;
93
94    now = time( NULL );
95    gettimeofday( &tv, NULL );
96
97#ifdef WIN32
98    now_tm = *localtime( &now );
99#else
100    localtime_r( &now, &now_tm );
101#endif
102    strftime( tmp, sizeof(tmp), "%H:%M:%S", &now_tm );
103    milliseconds = (int)(tv.tv_usec / 1000);
104    snprintf( buf, buflen, "%s.%03d", tmp, milliseconds );
105
106    return buf;
107}
108
109void
110tr_setMessageLevel( int level )
111{
112    tr_msgInit();
113    tr_lockLock( messageLock );
114    messageLevel = MAX( 0, level );
115    tr_lockUnlock( messageLock );
116}
117
118int tr_getMessageLevel( void )
119{
120    int ret;
121
122    tr_msgInit();
123    tr_lockLock( messageLock );
124    ret = messageLevel;
125    tr_lockUnlock( messageLock );
126
127    return ret;
128}
129
130void tr_setMessageQueuing( int enabled )
131{
132    tr_msgInit();
133    tr_lockLock( messageLock );
134    messageQueuing = enabled;
135    tr_lockUnlock( messageLock );
136}
137
138tr_msg_list * tr_getQueuedMessages( void )
139{
140    tr_msg_list * ret;
141
142    assert( NULL != messageLock );
143    tr_lockLock( messageLock );
144    ret = messageQueue;
145    messageQueue = NULL;
146    messageQueueTail = &messageQueue;
147    tr_lockUnlock( messageLock );
148
149    return ret;
150}
151
152void tr_freeMessageList( tr_msg_list * list )
153{
154    tr_msg_list * next;
155
156    while( NULL != list )
157    {
158        next = list->next;
159        free( list->message );
160        free( list );
161        list = next;
162    }
163}
164
165int
166tr_vasprintf( char **strp, const char *fmt, va_list ap )
167{
168    int ret;
169    struct evbuffer * buf = evbuffer_new( );
170    *strp = NULL;
171    if( evbuffer_add_vprintf( buf, fmt, ap ) < 0 )
172        ret = -1;
173    else {
174        ret = EVBUFFER_LENGTH( buf );
175        *strp = tr_strndup( (char*)EVBUFFER_DATA(buf), ret );
176    }
177    evbuffer_free( buf );
178    return ret;
179
180}
181
182int
183tr_asprintf( char **strp, const char *fmt, ...)
184{
185    int ret;
186    va_list ap;
187    va_start( ap, fmt );
188    ret = tr_vasprintf( strp, fmt, ap );
189    va_end( ap );
190    return ret;
191}
192
193void
194tr_msg( const char * file, int line, int level, const char * fmt, ... )
195{
196    FILE * fp;
197
198    assert( NULL != messageLock );
199    tr_lockLock( messageLock );
200
201    fp = tr_getLog( );
202
203    if( !messageLevel )
204    {
205        char * env = getenv( "TR_DEBUG" );
206        messageLevel = ( env ? atoi( env ) : 0 ) + 1;
207        messageLevel = MAX( 1, messageLevel );
208    }
209
210    if( messageLevel >= level )
211    {
212        va_list ap;
213        char * text;
214
215        /* build the text message */
216        va_start( ap, fmt );
217        tr_vasprintf( &text, fmt, ap );
218        va_end( ap );
219
220        if( text != NULL )
221        {
222            if( messageQueuing )
223            {
224                tr_msg_list * newmsg;
225                newmsg = tr_new0( tr_msg_list, 1 );
226                newmsg->level = level;
227                newmsg->when = time( NULL );
228                newmsg->message = text;
229                newmsg->file = file;
230                newmsg->line = line;
231
232                *messageQueueTail = newmsg;
233                messageQueueTail = &newmsg->next;
234            }
235            else
236            {
237                if( fp == NULL )
238                    fp = stderr;
239                fprintf( stderr, "%s\n", text );
240                tr_free( text );
241                fflush( fp );
242            }
243        }
244    }
245
246    tr_lockUnlock( messageLock );
247}
248
249int tr_rand( int sup )
250{
251    static int init = 0;
252
253    assert( sup > 0 );
254
255    if( !init )
256    {
257        srand( tr_date() );
258        init = 1;
259    }
260    return rand() % sup;
261}
262
263/***
264****
265***/
266
267void
268tr_set_compare( const void * va, size_t aCount,
269                const void * vb, size_t bCount,
270                int compare( const void * a, const void * b ),
271                size_t elementSize,
272                tr_set_func in_a_cb,
273                tr_set_func in_b_cb,
274                tr_set_func in_both_cb,
275                void * userData )
276{
277    const uint8_t * a = (const uint8_t *) va;
278    const uint8_t * b = (const uint8_t *) vb;
279    const uint8_t * aend = a + elementSize*aCount;
280    const uint8_t * bend = b + elementSize*bCount;
281
282    while( a!=aend || b!=bend )
283    {
284        if( a==aend )
285        {
286            (*in_b_cb)( (void*)b, userData );
287            b += elementSize;
288        }
289        else if ( b==bend )
290        {
291            (*in_a_cb)( (void*)a, userData );
292            a += elementSize;
293        }
294        else
295        {
296            const int val = (*compare)( a, b );
297
298            if( !val )
299            {
300                (*in_both_cb)( (void*)a, userData );
301                a += elementSize;
302                b += elementSize;
303            }
304            else if( val < 0 )
305            {
306                (*in_a_cb)( (void*)a, userData );
307                a += elementSize;
308            }
309            else if( val > 0 )
310            {
311                (*in_b_cb)( (void*)b, userData );
312                b += elementSize;
313            }
314        }
315    }
316}
317
318/***
319****
320***/
321
322int
323tr_compareUint16( uint16_t a, uint16_t b )
324{
325    if( a < b ) return -1;
326    if( a > b ) return 1;
327    return 0;
328}
329
330int
331tr_compareUint32( uint32_t a, uint32_t b )
332{
333    if( a < b ) return -1;
334    if( a > b ) return 1;
335    return 0;
336}
337
338/**
339***
340**/
341
342struct timeval
343timevalMsec( uint64_t milliseconds )
344{
345    struct timeval ret;
346    const uint64_t microseconds = milliseconds * 1000;
347    ret.tv_sec  = microseconds / 1000000;
348    ret.tv_usec = microseconds % 1000000;
349    return ret;
350}
351
352uint8_t *
353tr_loadFile( const char * path, size_t * size )
354{
355    uint8_t    * buf;
356    struct stat  sb;
357    FILE       * file;
358
359    /* try to stat the file */
360    errno = 0;
361    if( stat( path, &sb ) )
362    {
363        tr_err( "Couldn't get information for file \"%s\" %s", path, tr_strerror(errno) );
364        return NULL;
365    }
366
367    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
368    {
369        tr_err( "Not a regular file (%s)", path );
370        return NULL;
371    }
372
373    /* Load the torrent file into our buffer */
374    file = fopen( path, "rb" );
375    if( !file )
376    {
377        tr_err( "Couldn't open file \"%s\" %s", path, tr_strerror(errno) );
378        return NULL;
379    }
380    buf = malloc( sb.st_size );
381    if( NULL == buf )
382    {
383        tr_err( "Couldn't allocate memory (%"PRIu64" bytes)",
384                ( uint64_t )sb.st_size );
385        fclose( file );
386    }
387    fseek( file, 0, SEEK_SET );
388    if( fread( buf, sb.st_size, 1, file ) != 1 )
389    {
390        tr_err( "Error reading \"%s\" %s", path, tr_strerror(errno) );
391        free( buf );
392        fclose( file );
393        return NULL;
394    }
395    fclose( file );
396
397    *size = sb.st_size;
398
399    return buf;
400}
401
402int
403tr_mkdir( const char * path, int permissions
404#ifdef WIN32
405                                             UNUSED
406#endif
407                                                    )
408{
409#ifdef WIN32
410    return mkdir( path );
411#else
412    return mkdir( path, permissions );
413#endif
414}
415
416int
417tr_mkdirp( const char * path_in, int permissions )
418{
419    char * path = tr_strdup( path_in );
420    char * p, * pp;
421    struct stat sb;
422    int done;
423
424    /* walk past the root */
425    p = path;
426    while( *p == TR_PATH_DELIMITER )
427        ++p;
428
429    pp = p;
430    done = 0;
431    while( ( p = strchr( pp, TR_PATH_DELIMITER ) ) || ( p = strchr( pp, '\0' ) ) )
432    {
433        if( !*p )
434            done = 1;
435        else
436            *p = '\0';
437
438        if( stat( path, &sb ) )
439        {
440            /* Folder doesn't exist yet */
441            if( tr_mkdir( path, permissions ) ) {
442                const int err = errno;
443                tr_err( "Couldn't create directory %s (%s)", path, tr_strerror( err ) );
444                tr_free( path );
445                errno = err;
446                return -1;
447            }
448        }
449        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
450        {
451            /* Node exists but isn't a folder */
452            tr_err( "Remove %s, it's in the way.", path );
453            tr_free( path );
454            errno = ENOTDIR;
455            return -1;
456        }
457
458        if( done )
459            break;
460
461        *p = TR_PATH_DELIMITER;
462        p++;
463        pp = p;
464    }
465
466    tr_free( path );
467    return 0;
468}
469
470void
471tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... )
472{
473    struct evbuffer * evbuf = evbuffer_new( );
474    const char * element = first_element;
475    va_list vl;
476    va_start( vl, first_element );
477    while( element ) {
478        if( EVBUFFER_LENGTH(evbuf) )
479            evbuffer_add_printf( evbuf, "%c", TR_PATH_DELIMITER );
480        evbuffer_add_printf( evbuf, "%s", element );
481        element = (const char*) va_arg( vl, const char* );
482    }
483    if( EVBUFFER_LENGTH(evbuf) )
484        strlcpy( buf, (char*)EVBUFFER_DATA(evbuf), buflen );
485    else
486        *buf = '\0';
487    evbuffer_free( evbuf );
488}
489
490int
491tr_ioErrorFromErrno( int err )
492{
493    switch( err )
494    {
495        case 0:
496            return TR_OK;
497        case EACCES:
498        case EROFS:
499            return TR_ERROR_IO_PERMISSIONS;
500        case ENOSPC:
501            return TR_ERROR_IO_SPACE;
502        case EMFILE:
503            return TR_ERROR_IO_OPEN_FILES;
504        case EFBIG:
505            return TR_ERROR_IO_FILE_TOO_BIG;
506        default:
507            tr_dbg( "generic i/o errno from errno: %s", tr_strerror( errno ) );
508            return TR_ERROR_IO_OTHER;
509    }
510}
511
512const char *
513tr_errorString( int code )
514{
515    switch( code )
516    {
517        case TR_OK:
518            return "No error";
519
520        case TR_ERROR:
521            return "Generic error";
522        case TR_ERROR_ASSERT:
523            return "Assert error";
524
525        case TR_ERROR_IO_PARENT:
526            return "Download folder does not exist";
527        case TR_ERROR_IO_PERMISSIONS:
528            return "Insufficient permissions";
529        case TR_ERROR_IO_SPACE:
530            return "Insufficient free space";
531        case TR_ERROR_IO_FILE_TOO_BIG:
532            return "File too large";
533        case TR_ERROR_IO_OPEN_FILES:
534            return "Too many open files";
535        case TR_ERROR_IO_DUP_DOWNLOAD:
536            return "Already active transfer with same name and download folder";
537        case TR_ERROR_IO_OTHER:
538            return "Generic I/O error";
539
540        default:
541            return "Unknown error";
542    }
543}
544
545/****
546*****
547****/
548
549char*
550tr_strdup( const char * in )
551{
552    return tr_strndup( in, in ? strlen(in) : 0 );
553}
554
555char*
556tr_strndup( const char * in, int len )
557{
558    char * out = NULL;
559
560    if( len < 0 )
561    {
562        out = tr_strdup( in );
563    }
564    else if( in != NULL )
565    {
566        out = tr_malloc( len+1 );
567        memcpy( out, in, len );
568        out[len] = '\0';
569    }
570    return out;
571}
572
573void*
574tr_calloc( size_t nmemb, size_t size )
575{
576    return nmemb && size ? calloc( nmemb, size ) : NULL;
577}
578
579void*
580tr_malloc( size_t size )
581{
582    return size ? malloc( size ) : NULL;
583}
584
585void*
586tr_malloc0( size_t size )
587{
588    void * ret = tr_malloc( size );
589    memset( ret, 0, size );
590    return ret;
591}
592
593void
594tr_free( void * p )
595{
596    if( p )
597        free( p );
598}
599
600const char*
601tr_strerror( int i )
602{
603    const char * ret = strerror( i );
604    if( ret == NULL )
605        ret = "Unknown Error";
606    return ret;
607}
608
609/****
610*****
611****/
612
613/* note that the argument is how many bits are needed, not bytes */
614tr_bitfield*
615tr_bitfieldNew( size_t bitcount )
616{
617    tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) );
618    if( NULL == ret )
619        return NULL;
620
621    ret->len = (bitcount+7u) / 8u;
622    ret->bits = calloc( ret->len, 1 );
623    if( NULL == ret->bits ) {
624        free( ret );
625        return NULL;
626    }
627
628    return ret;
629}
630
631tr_bitfield*
632tr_bitfieldDup( const tr_bitfield * in )
633{
634    tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) );
635    ret->len = in->len;
636    ret->bits = malloc( ret->len );
637    memcpy( ret->bits, in->bits, ret->len );
638    return ret;
639}
640
641void tr_bitfieldFree( tr_bitfield * bitfield )
642{
643    if( bitfield )
644    {
645        free( bitfield->bits );
646        free( bitfield );
647    }
648}
649
650void
651tr_bitfieldClear( tr_bitfield * bitfield )
652{
653    memset( bitfield->bits, 0, bitfield->len );
654}
655
656int
657tr_bitfieldIsEmpty( const tr_bitfield * bitfield )
658{
659    size_t i;
660
661    for( i=0; i<bitfield->len; ++i )
662        if( bitfield->bits[i] )
663            return 0;
664
665    return 1;
666}
667
668int
669tr_bitfieldHas( const tr_bitfield * bitfield, size_t nth )
670{
671    static const uint8_t ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
672    return bitfield!=NULL && (bitfield->bits[nth>>3u] & ands[nth&7u] );
673}
674
675void
676tr_bitfieldAdd( tr_bitfield  * bitfield, size_t nth )
677{
678    static const uint8_t ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
679    bitfield->bits[nth>>3u] |= ands[nth&7u];
680    assert( tr_bitfieldHas( bitfield, nth ) );
681}
682
683void
684tr_bitfieldAddRange( tr_bitfield  * bitfield,
685                     size_t         begin,
686                     size_t         end )
687{
688    /* TODO: there are faster ways to do this */
689    size_t i;
690    for( i=begin; i<end; ++i )
691        tr_bitfieldAdd( bitfield, i );
692}
693
694void
695tr_bitfieldRem( tr_bitfield   * bitfield,
696                size_t          nth )
697{
698    static const uint8_t rems[8] = { 127, 191, 223, 239, 247, 251, 253, 254 };
699
700    if( bitfield != NULL )
701        bitfield->bits[nth>>3u] &= rems[nth&7u];
702
703    assert( !tr_bitfieldHas( bitfield, nth ) );
704}
705
706void
707tr_bitfieldRemRange ( tr_bitfield  * b,
708                      size_t         begin,
709                      size_t         end )
710{
711    /* TODO: there are faster ways to do this */
712    size_t i;
713    for( i=begin; i<end; ++i )
714        tr_bitfieldRem( b, i );
715}
716
717tr_bitfield*
718tr_bitfieldOr( tr_bitfield * a, const tr_bitfield * b )
719{
720    uint8_t *ait;
721    const uint8_t *aend, *bit;
722
723    assert( a->len == b->len );
724
725    for( ait=a->bits, bit=b->bits, aend=ait+a->len; ait!=aend; )
726        *ait++ |= *bit++;
727
728    return a;
729}
730
731size_t
732tr_bitfieldCountTrueBits( const tr_bitfield* b )
733{
734    size_t ret = 0;
735    const uint8_t *it, *end;
736    static const int trueBitCount[512] = {
737        0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
738        1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
739        1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
740        2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
741        1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
742        2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
743        2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
744        3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
745        1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
746        2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
747        2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
748        3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
749        2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
750        3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
751        3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,
752        4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,5,6,6,7,6,7,7,8,6,7,7,8,7,8,8,9
753    };
754
755    if( !b )
756        return 0;
757
758    for( it=b->bits, end=it+b->len; it!=end; ++it )
759        ret += trueBitCount[*it];
760
761    return ret;
762}
763
764/***
765****
766***/
767
768uint64_t
769tr_date( void )
770{
771    struct timeval tv;
772    gettimeofday( &tv, NULL );
773    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
774}
775
776void
777tr_wait( uint64_t delay_milliseconds )
778{
779#ifdef __BEOS__
780    snooze( 1000 * delay_milliseconds );
781#elif defined(WIN32)
782    Sleep( (DWORD)delay_milliseconds );
783#else
784    usleep( 1000 * delay_milliseconds );
785#endif
786}
787
788/***
789****
790***/
791
792
793#ifndef HAVE_STRLCPY
794
795/*
796 * Copy src to string dst of size siz.  At most siz-1 characters
797 * will be copied.  Always NUL terminates (unless siz == 0).
798 * Returns strlen(src); if retval >= siz, truncation occurred.
799 */
800size_t
801strlcpy(char *dst, const char *src, size_t siz)
802{
803        char *d = dst;
804        const char *s = src;
805        size_t n = siz;
806
807        /* Copy as many bytes as will fit */
808        if (n != 0) {
809                while (--n != 0) {
810                        if ((*d++ = *s++) == '\0')
811                                break;
812                }
813        }
814
815        /* Not enough room in dst, add NUL and traverse rest of src */
816        if (n == 0) {
817                if (siz != 0)
818                        *d = '\0';              /* NUL-terminate dst */
819                while (*s++)
820                        ;
821        }
822
823        return(s - src - 1);    /* count does not include NUL */
824}
825
826#endif /* HAVE_STRLCPY */
827
828
829#ifndef HAVE_STRLCAT
830
831/*
832 * Appends src to string dst of size siz (unlike strncat, siz is the
833 * full size of dst, not space left).  At most siz-1 characters
834 * will be copied.  Always NUL terminates (unless siz <= strlen(dst)).
835 * Returns strlen(src) + MIN(siz, strlen(initial dst)).
836 * If retval >= siz, truncation occurred.
837 */
838size_t
839strlcat(char *dst, const char *src, size_t siz)
840{
841        char *d = dst;
842        const char *s = src;
843        size_t n = siz;
844        size_t dlen;
845
846        /* Find the end of dst and adjust bytes left but don't go past end */
847        while (n-- != 0 && *d != '\0')
848                d++;
849        dlen = d - dst;
850        n = siz - dlen;
851
852        if (n == 0)
853                return(dlen + strlen(s));
854        while (*s != '\0') {
855                if (n != 1) {
856                        *d++ = *s;
857                        n--;
858                }
859                s++;
860        }
861        *d = '\0';
862
863        return(dlen + (s - src));       /* count does not include NUL */
864}
865
866#endif /* HAVE_STRLCAT */
867
868/***
869****
870***/
871
872double
873tr_getRatio( double numerator, double denominator )
874{
875    double ratio;
876
877    if( denominator )
878        ratio = numerator / denominator;
879    else if( numerator )
880        ratio = TR_RATIO_INF;
881    else
882        ratio = TR_RATIO_NA;
883
884    return ratio;
885}
886
887void
888tr_sha1_to_hex( char * out, const uint8_t * sha1 )
889{
890    static const char hex[] = "0123456789abcdef";
891    int i;
892    for (i = 0; i < 20; i++) {
893        unsigned int val = *sha1++;
894        *out++ = hex[val >> 4];
895        *out++ = hex[val & 0xf];
896    }
897    *out = '\0';
898}
Note: See TracBrowser for help on using the repository browser.