source: trunk/libtransmission/utils.c @ 3753

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

update our #includes now that libevent has cleaned up event.h

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