source: trunk/libtransmission/utils.c @ 3747

Last change on this file since 3747 was 3747, checked in by charles, 15 years ago

fix libevent #include quirk reported by SoftwareElves?

  • Property svn:keywords set to Date Rev Author Id
File size: 17.1 KB
Line 
1/******************************************************************************
2 * $Id: utils.c 3747 2007-11-07 03:13:04Z charles $
3 *
4 * Copyright (c) 2005-2007 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 <ctype.h>
27#include <errno.h>
28#include <stdarg.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32
33#include <sys/time.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <unistd.h> /* usleep, stat */
37
38#include <sys/queue.h> /* libevent needs this */
39#include <sys/types.h> /* libevent needs this */
40#include "event.h"
41
42#ifdef WIN32
43    #include <windows.h> /* for Sleep */
44#elif defined(__BEOS__)
45    #include <kernel/OS.h>
46#endif
47
48#include "transmission.h"
49#include "trcompat.h"
50#include "utils.h"
51#include "platform.h"
52
53static tr_lock      * messageLock = NULL;
54static int            messageLevel = 0;
55static int            messageQueuing = FALSE;
56static tr_msg_list *  messageQueue = NULL;
57static tr_msg_list ** messageQueueTail = &messageQueue;
58
59void tr_msgInit( void )
60{
61    if( !messageLock )
62         messageLock = tr_lockNew( );
63}
64
65FILE*
66tr_getLog( void )
67{
68    static int initialized = FALSE;
69    static FILE * file= NULL;
70
71    if( !initialized )
72    {
73        const char * str = getenv( "TR_DEBUG_FD" );
74        int fd;
75        if( str && *str )
76            fd = atoi( str );
77        switch( fd ) {
78            case 1: file = stdout; break;
79            case 2: file = stderr; break;
80            default: file = NULL; break;
81        }
82        initialized = TRUE;
83    }
84
85    return file;
86}
87
88char*
89tr_getLogTimeStr( char * buf, int buflen )
90{
91    char tmp[64];
92    time_t now;
93    struct tm now_tm;
94    struct timeval tv;
95    int milliseconds;
96
97    now = time( NULL );
98    gettimeofday( &tv, NULL );
99
100    localtime_r( &now, &now_tm );
101    strftime( tmp, sizeof(tmp), "%H:%M:%S", &now_tm );
102    milliseconds = (int)(tv.tv_usec / 1000);
103    snprintf( buf, buflen, "%s.%03d", tmp, milliseconds );
104
105    return buf;
106}
107
108void
109tr_setMessageLevel( int level )
110{
111    tr_msgInit();
112    tr_lockLock( messageLock );
113    messageLevel = MAX( 0, level );
114    tr_lockUnlock( messageLock );
115}
116
117int tr_getMessageLevel( void )
118{
119    int ret;
120
121    tr_msgInit();
122    tr_lockLock( messageLock );
123    ret = messageLevel;
124    tr_lockUnlock( messageLock );
125
126    return ret;
127}
128
129void tr_setMessageQueuing( int enabled )
130{
131    tr_msgInit();
132    tr_lockLock( messageLock );
133    messageQueuing = enabled;
134    tr_lockUnlock( messageLock );
135}
136
137tr_msg_list * tr_getQueuedMessages( void )
138{
139    tr_msg_list * ret;
140
141    assert( NULL != messageLock );
142    tr_lockLock( messageLock );
143    ret = messageQueue;
144    messageQueue = NULL;
145    messageQueueTail = &messageQueue;
146    tr_lockUnlock( messageLock );
147
148    return ret;
149}
150
151void tr_freeMessageList( tr_msg_list * list )
152{
153    tr_msg_list * next;
154
155    while( NULL != list )
156    {
157        next = list->next;
158        free( list->message );
159        free( list );
160        list = next;
161    }
162}
163
164int
165tr_vasprintf( char **strp, const char *fmt, va_list ap )
166{
167    int ret;
168    struct evbuffer * buf = evbuffer_new( );
169    *strp = NULL;
170    if( evbuffer_add_vprintf( buf, fmt, ap ) < 0 )
171        ret = -1;
172    else {
173        ret = EVBUFFER_LENGTH( buf );
174        *strp = tr_strndup( (char*)EVBUFFER_DATA(buf), ret );
175    }
176    evbuffer_free( buf );
177    return ret;
178
179}
180
181int
182tr_asprintf( char **strp, const char *fmt, ...)
183{
184    int ret;
185    va_list ap;
186    va_start( ap, fmt );
187    ret = tr_vasprintf( strp, fmt, ap );
188    va_end( ap );
189    return ret;
190}
191
192void tr_msg( int level, const char * fmt, ... )
193{
194    FILE * fp;
195
196    assert( NULL != messageLock );
197    tr_lockLock( messageLock );
198
199    fp = tr_getLog( );
200
201    if( !messageLevel )
202    {
203        char * env = getenv( "TR_DEBUG" );
204        messageLevel = ( env ? atoi( env ) : 0 ) + 1;
205        messageLevel = MAX( 1, messageLevel );
206    }
207
208    if( messageLevel >= level )
209    {
210        va_list ap;
211        char * text;
212
213        /* build the text message */
214        va_start( ap, fmt );
215        tr_vasprintf( &text, fmt, ap );
216        va_end( ap );
217
218        if( text != NULL )
219        {
220            if( messageQueuing )
221            {
222                tr_msg_list * newmsg;
223                newmsg = tr_new0( tr_msg_list, 1 );
224                newmsg->level = level;
225                newmsg->when = time( NULL );
226                newmsg->message = text;
227
228                *messageQueueTail = newmsg;
229                messageQueueTail = &newmsg->next;
230            }
231            else
232            {
233                if( fp == NULL )
234                    fp = stderr;
235                fprintf( stderr, "%s\n", text );
236                tr_free( text );
237                fflush( fp );
238            }
239        }
240    }
241
242    tr_lockUnlock( messageLock );
243}
244
245int tr_rand( int sup )
246{
247    static int init = 0;
248
249    assert( sup > 0 );
250
251    if( !init )
252    {
253        srand( tr_date() );
254        init = 1;
255    }
256    return rand() % sup;
257}
258
259/***
260****
261***/
262
263void
264tr_set_compare( const void * va, size_t aCount,
265                const void * vb, size_t bCount,
266                int compare( const void * a, const void * b ),
267                size_t elementSize,
268                tr_set_func in_a_cb,
269                tr_set_func in_b_cb,
270                tr_set_func in_both_cb,
271                void * userData )
272{
273    const uint8_t * a = (const uint8_t *) va;
274    const uint8_t * b = (const uint8_t *) vb;
275    const uint8_t * aend = a + elementSize*aCount;
276    const uint8_t * bend = b + elementSize*bCount;
277
278    while( a!=aend || b!=bend )
279    {
280        if( a==aend )
281        {
282            (*in_b_cb)( (void*)b, userData );
283            b += elementSize;
284        }
285        else if ( b==bend )
286        {
287            (*in_a_cb)( (void*)a, userData );
288            a += elementSize;
289        }
290        else
291        {
292            const int val = (*compare)( a, b );
293
294            if( !val )
295            {
296                (*in_both_cb)( (void*)a, userData );
297                a += elementSize;
298                b += elementSize;
299            }
300            else if( val < 0 )
301            {
302                (*in_a_cb)( (void*)a, userData );
303                a += elementSize;
304            }
305            else if( val > 0 )
306            {
307                (*in_b_cb)( (void*)b, userData );
308                b += elementSize;
309            }
310        }
311    }
312}
313
314/***
315****
316***/
317
318int
319tr_compareUint16( uint16_t a, uint16_t b )
320{
321    if( a < b ) return -1;
322    if( a > b ) return 1;
323    return 0;
324}
325
326int
327tr_compareUint32( uint32_t a, uint32_t b )
328{
329    if( a < b ) return -1;
330    if( a > b ) return 1;
331    return 0;
332}
333
334/**
335***
336**/
337
338struct timeval
339timevalMsec( uint64_t milliseconds )
340{
341    struct timeval ret;
342    const uint64_t microseconds = milliseconds * 1000;
343    ret.tv_sec  = microseconds / 1000000;
344    ret.tv_usec = microseconds % 1000000;
345    return ret;
346}
347
348uint8_t *
349tr_loadFile( const char * path, size_t * size )
350{
351    uint8_t    * buf;
352    struct stat  sb;
353    FILE       * file;
354
355    /* try to stat the file */
356    errno = 0;
357    if( stat( path, &sb ) )
358    {
359        tr_err( "Couldn't get information for file \"%s\" %s", path, strerror(errno) );
360        return NULL;
361    }
362
363    if( ( sb.st_mode & S_IFMT ) != S_IFREG )
364    {
365        tr_err( "Not a regular file (%s)", path );
366        return NULL;
367    }
368
369    /* Load the torrent file into our buffer */
370    file = fopen( path, "rb" );
371    if( !file )
372    {
373        tr_err( "Couldn't open file \"%s\" %s", path, strerror(errno) );
374        return NULL;
375    }
376    buf = malloc( sb.st_size );
377    if( NULL == buf )
378    {
379        tr_err( "Couldn't allocate memory (%"PRIu64" bytes)",
380                ( uint64_t )sb.st_size );
381        fclose( file );
382    }
383    fseek( file, 0, SEEK_SET );
384    if( fread( buf, sb.st_size, 1, file ) != 1 )
385    {
386        tr_err( "Error reading \"%s\" %s", path, strerror(errno) );
387        free( buf );
388        fclose( file );
389        return NULL;
390    }
391    fclose( file );
392
393    *size = sb.st_size;
394
395    return buf;
396}
397
398int
399tr_mkdir( const char * path, int permissions
400#ifdef WIN32
401                                             UNUSED
402#endif
403                                                    )
404{
405#ifdef WIN32
406    return mkdir( path );
407#else
408    return mkdir( path, permissions );
409#endif
410}
411
412int
413tr_mkdirp( const char * path_in, int permissions )
414{
415    char * path = tr_strdup( path_in );
416    char * p, * pp;
417    struct stat sb;
418    int done;
419
420    /* walk past the root */
421    p = path;
422    while( *p == TR_PATH_DELIMITER )
423        ++p;
424
425    pp = p;
426    done = 0;
427    while( ( p = strchr( pp, TR_PATH_DELIMITER ) ) || ( p = strchr( pp, '\0' ) ) )
428    {
429        if( !*p )
430            done = 1;
431        else
432            *p = '\0';
433
434        if( stat( path, &sb ) )
435        {
436            /* Folder doesn't exist yet */
437            if( tr_mkdir( path, permissions ) )
438            {
439                tr_err( "Could not create directory %s (%s)", path,
440                        strerror( errno ) );
441                tr_free( path );
442                return 1;
443            }
444        }
445        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
446        {
447            /* Node exists but isn't a folder */
448            tr_err( "Remove %s, it's in the way.", path );
449            tr_free( path );
450            return 1;
451        }
452
453        if( done )
454            break;
455
456        *p = TR_PATH_DELIMITER;
457        p++;
458        pp = p;
459    }
460
461    tr_free( path );
462    return 0;
463}
464
465void
466tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... )
467{
468    va_list vl;
469    char* walk = buf;
470    const char * element = first_element;
471
472    if( first_element == NULL )
473        return;
474
475    va_start( vl, first_element );
476    for( ;; ) {
477        const size_t n = strlen( element );
478        memcpy( walk, element, n );
479        walk += n;
480        element = (const char*) va_arg( vl, const char* );
481        if( element == NULL )
482            break;
483        *walk++ = TR_PATH_DELIMITER;
484    }
485    *walk = '\0';
486    assert( walk-buf <= (int)buflen );
487}
488
489int
490tr_ioErrorFromErrno( void )
491{
492    switch( errno )
493    {
494        case EACCES:
495        case EROFS:
496            return TR_ERROR_IO_PERMISSIONS;
497        case ENOSPC:
498            return TR_ERROR_IO_SPACE;
499        case EMFILE:
500            return TR_ERROR_IO_OPEN_FILES;
501        case EFBIG:
502            return TR_ERROR_IO_FILE_TOO_BIG;
503        default:
504            tr_dbg( "generic i/o errno from errno: %s", strerror( errno ) );
505            return TR_ERROR_IO_OTHER;
506    }
507}
508
509char *
510tr_errorString( int code )
511{
512    switch( code )
513    {
514        case TR_OK:
515            return "No error";
516        case TR_ERROR:
517            return "Generic error";
518        case TR_ERROR_ASSERT:
519            return "Assert error";
520        case TR_ERROR_IO_PERMISSIONS:
521            return "Insufficient permissions";
522        case TR_ERROR_IO_SPACE:
523            return "Insufficient free space";
524        case TR_ERROR_IO_DUP_DOWNLOAD:
525            return "Already active transfer with same name and download folder";
526        case TR_ERROR_IO_FILE_TOO_BIG:
527            return "File too large";
528        case TR_ERROR_IO_OPEN_FILES:
529            return "Too many open files";
530        case TR_ERROR_IO_OTHER:
531            return "Generic I/O error";
532    }
533    return "Unknown error";
534}
535
536/****
537*****
538****/
539
540char*
541tr_strdup( const char * in )
542{
543    return tr_strndup( in, in ? strlen(in) : 0 );
544}
545
546char*
547tr_strndup( const char * in, int len )
548{
549    char * out = NULL;
550
551    if( in != NULL )
552    {
553        out = tr_malloc( len+1 );
554        memcpy( out, in, len );
555        out[len] = '\0';
556    }
557    return out;
558}
559
560void*
561tr_calloc( size_t nmemb, size_t size )
562{
563    return nmemb && size ? calloc( nmemb, size ) : NULL;
564}
565
566void*
567tr_malloc( size_t size )
568{
569    return size ? malloc( size ) : NULL;
570}
571
572void*
573tr_malloc0( size_t size )
574{
575    void * ret = tr_malloc( size );
576    memset( ret, 0, size );
577    return ret;
578}
579
580void
581tr_free( void * p )
582{
583    if( p )
584        free( p );
585}
586
587/****
588*****
589****/
590
591/* note that the argument is how many bits are needed, not bytes */
592tr_bitfield*
593tr_bitfieldNew( size_t bitcount )
594{
595    tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) );
596    if( NULL == ret )
597        return NULL;
598
599    ret->len = (bitcount+7u) / 8u;
600    ret->bits = calloc( ret->len, 1 );
601    if( NULL == ret->bits ) {
602        free( ret );
603        return NULL;
604    }
605
606    return ret;
607}
608
609tr_bitfield*
610tr_bitfieldDup( const tr_bitfield * in )
611{
612    tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) );
613    ret->len = in->len;
614    ret->bits = malloc( ret->len );
615    memcpy( ret->bits, in->bits, ret->len );
616    return ret;
617}
618
619void tr_bitfieldFree( tr_bitfield * bitfield )
620{
621    if( bitfield )
622    {
623        free( bitfield->bits );
624        free( bitfield );
625    }
626}
627
628void
629tr_bitfieldClear( tr_bitfield * bitfield )
630{
631    memset( bitfield->bits, 0, bitfield->len );
632}
633
634int
635tr_bitfieldIsEmpty( const tr_bitfield * bitfield )
636{
637    unsigned int i;
638
639    for( i=0; i<bitfield->len; ++i )
640        if( bitfield->bits[i] )
641            return 0;
642
643    return 1;
644}
645
646int
647tr_bitfieldHas( const tr_bitfield * bitfield, size_t nth )
648{
649    static const uint8_t ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
650    return bitfield!=NULL && (bitfield->bits[nth>>3u] & ands[nth&7u] );
651}
652
653void
654tr_bitfieldAdd( tr_bitfield  * bitfield, size_t nth )
655{
656    static const uint8_t ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
657    bitfield->bits[nth>>3u] |= ands[nth&7u];
658    assert( tr_bitfieldHas( bitfield, nth ) );
659}
660
661void
662tr_bitfieldAddRange( tr_bitfield  * bitfield,
663                     size_t         begin,
664                     size_t         end )
665{
666    /* TODO: there are faster ways to do this */
667    unsigned int i;
668    for( i=begin; i<end; ++i )
669        tr_bitfieldAdd( bitfield, i );
670}
671
672void
673tr_bitfieldRem( tr_bitfield   * bitfield,
674                size_t          nth )
675{
676    static const uint8_t rems[8] = { 127, 191, 223, 239, 247, 251, 253, 254 };
677
678    if( bitfield != NULL )
679        bitfield->bits[nth>>3u] &= rems[nth&7u];
680
681    assert( !tr_bitfieldHas( bitfield, nth ) );
682}
683
684void
685tr_bitfieldRemRange ( tr_bitfield  * b,
686                      size_t         begin,
687                      size_t         end )
688{
689    /* TODO: there are faster ways to do this */
690    unsigned int i;
691    for( i=begin; i<end; ++i )
692        tr_bitfieldRem( b, i );
693}
694
695tr_bitfield*
696tr_bitfieldAnd( tr_bitfield * a, const tr_bitfield * b )
697{
698    uint8_t *ait;
699    const uint8_t *aend, *bit;
700
701    assert( a->len == b->len );
702
703    for( ait=a->bits, bit=b->bits, aend=ait+a->len; ait!=aend; ++ait, ++bit )
704        *ait &= *bit;
705
706    return a;
707}
708
709size_t
710tr_bitfieldCountTrueBits( const tr_bitfield* b )
711{
712    size_t ret = 0;
713    const uint8_t *it, *end;
714    static const int trueBitCount[512] = {
715        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,
716        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,
717        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,
718        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,
719        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,
720        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,
721        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,
722        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,
723        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,
724        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,
725        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,
726        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,
727        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,
728        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,
729        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,
730        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
731    };
732
733    if( !b )
734        return 0;
735
736    for( it=b->bits, end=it+b->len; it!=end; ++it )
737        ret += trueBitCount[*it];
738
739    return ret;
740}
741
742/***
743****
744***/
745
746uint64_t
747tr_date( void )
748{
749    struct timeval tv;
750    gettimeofday( &tv, NULL );
751    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
752}
753
754void
755tr_wait( uint64_t delay_milliseconds )
756{
757#ifdef __BEOS__
758    snooze( 1000 * delay_milliseconds );
759#elif defined(WIN32)
760    Sleep( (DWORD)delay_milliseconds );
761#else
762    usleep( 1000 * delay_milliseconds );
763#endif
764}
Note: See TracBrowser for help on using the repository browser.