source: trunk/libtransmission/utils.c @ 2348

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

fix crash on zero-byte torrents

  • Property svn:keywords set to Date Rev Author Id
File size: 14.2 KB
Line 
1/******************************************************************************
2 * $Id: utils.c 2348 2007-07-15 03:52:51Z 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 <ctype.h>
26#include <stdarg.h>
27#include <sys/time.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <unistd.h> /* usleep, stat */
31#include "transmission.h"
32
33#define SPRINTF_BUFSIZE         100
34
35static tr_lock_t      * messageLock = NULL;
36static int              messageLevel = 0;
37static int              messageQueuing = 0;
38static tr_msg_list_t *  messageQueue = NULL;
39static tr_msg_list_t ** messageQueueTail = &messageQueue;
40
41void tr_msgInit( void )
42{
43    if( NULL == messageLock )
44    {
45        messageLock = calloc( 1, sizeof( *messageLock ) );
46        tr_lockInit( messageLock );
47    }
48}
49
50void tr_setMessageLevel( int level )
51{
52    tr_msgInit();
53    tr_lockLock( messageLock );
54    messageLevel = MAX( 0, level );
55    tr_lockUnlock( messageLock );
56}
57
58int tr_getMessageLevel( void )
59{
60    int ret;
61
62    tr_msgInit();
63    tr_lockLock( messageLock );
64    ret = messageLevel;
65    tr_lockUnlock( messageLock );
66
67    return ret;
68}
69
70void tr_setMessageQueuing( int enabled )
71{
72    tr_msgInit();
73    tr_lockLock( messageLock );
74    messageQueuing = enabled;
75    tr_lockUnlock( messageLock );
76}
77
78tr_msg_list_t * tr_getQueuedMessages( void )
79{
80    tr_msg_list_t * ret;
81
82    assert( NULL != messageLock );
83    tr_lockLock( messageLock );
84    ret = messageQueue;
85    messageQueue = NULL;
86    messageQueueTail = &messageQueue;
87    tr_lockUnlock( messageLock );
88
89    return ret;
90}
91
92void tr_freeMessageList( tr_msg_list_t * list )
93{
94    tr_msg_list_t * next;
95
96    while( NULL != list )
97    {
98        next = list->next;
99        free( list->message );
100        free( list );
101        list = next;
102    }
103}
104
105void tr_msg( int level, char * msg, ... )
106{
107    va_list         args1, args2;
108    tr_msg_list_t * newmsg;
109    int             len1, len2;
110
111    assert( NULL != messageLock );
112    tr_lockLock( messageLock );
113
114    if( !messageLevel )
115    {
116        char * env;
117        env          = getenv( "TR_DEBUG" );
118        messageLevel = ( env ? atoi( env ) : 0 ) + 1;
119        messageLevel = MAX( 1, messageLevel );
120    }
121
122    if( messageLevel >= level )
123    {
124        va_start( args1, msg );
125        if( messageQueuing )
126        {
127            newmsg = calloc( 1, sizeof( *newmsg ) );
128            if( NULL != newmsg )
129            {
130                newmsg->level = level;
131                newmsg->when = time( NULL );
132                len1 = len2 = 0;
133                va_start( args2, msg );
134                tr_vsprintf( &newmsg->message, &len1, &len2, msg,
135                             args1, args2 );
136                va_end( args2 );
137                if( NULL == newmsg->message )
138                {
139                    free( newmsg );
140                }
141                else
142                {
143                    *messageQueueTail = newmsg;
144                    messageQueueTail = &newmsg->next;
145                }
146            }
147        }
148        else
149        {
150            vfprintf( stderr, msg, args1 );
151            fputc( '\n', stderr );
152        }
153        va_end( args1 );
154    }
155
156    tr_lockUnlock( messageLock );
157}
158
159int tr_rand( int sup )
160{
161    static int init = 0;
162    if( !init )
163    {
164        srand( tr_date() );
165        init = 1;
166    }
167    return rand() % sup;
168}
169
170
171void*
172tr_memmem( const void* haystack, size_t hl,
173           const void* needle,   size_t nl)
174{
175    const char *walk, *end;
176
177    if( !nl )
178        return (void*) haystack;
179
180    if( hl < nl )
181        return NULL;
182
183    for (walk=(const char*)haystack, end=walk+hl-nl; walk!=end; ++walk)
184        if( !memcmp( walk, needle, nl ) )
185            return (void*) walk;
186
187    return NULL;
188}
189
190int tr_mkdir( char * path )
191{
192    char      * p, * pp;
193    struct stat sb;
194    int done;
195
196    p = path;
197    while( '/' == *p )
198      p++;
199    pp = p;
200    done = 0;
201    while( ( p = strchr( pp, '/' ) ) || ( p = strchr( pp, '\0' ) ) )
202    {
203        if( '\0' == *p)
204        {
205            done = 1;
206        }
207        else
208        {
209            *p = '\0';
210        }
211        if( stat( path, &sb ) )
212        {
213            /* Folder doesn't exist yet */
214            if( mkdir( path, 0777 ) )
215            {
216                tr_err( "Could not create directory %s (%s)", path,
217                        strerror( errno ) );
218                *p = '/';
219                return 1;
220            }
221        }
222        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
223        {
224            /* Node exists but isn't a folder */
225            tr_err( "Remove %s, it's in the way.", path );
226            *p = '/';
227            return 1;
228        }
229        if( done )
230        {
231            break;
232        }
233        *p = '/';
234        p++;
235        pp = p;
236    }
237
238    return 0;
239}
240
241int
242tr_strncasecmp( const char * s1, const char * s2, size_t n )
243{
244    if ( !n )
245        return 0;
246
247    while( n-- != 0 && tolower( *s1 ) == tolower( *s2 ) ) {
248        if( !n || !*s1 || !*s2 )
249            break;
250        ++s1;
251        ++s2;
252    }
253
254    return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
255}
256
257int tr_sprintf( char ** buf, int * used, int * max, const char * format, ... )
258{
259    va_list ap1, ap2;
260    int     ret;
261
262    va_start( ap1, format );
263    va_start( ap2, format );
264    ret = tr_vsprintf( buf, used, max, format, ap1, ap2 );
265    va_end( ap2 );
266    va_end( ap1 );
267
268    return ret;
269}
270
271int tr_vsprintf( char ** buf, int * used, int * max, const char * fmt,
272                 va_list ap1, va_list ap2 )
273{
274    int     want;
275
276    want = vsnprintf( NULL, 0, fmt, ap1 );
277
278    if( tr_concat( buf, used, max, NULL, want ) )
279    {
280        return 1;
281    }
282    assert( *used + want + 1 <= *max );
283
284    *used += vsnprintf( *buf + *used, *max - *used, fmt, ap2 );
285
286    return 0;
287}
288
289int tr_concat( char ** buf, int * used, int * max, const char * data, int len )
290{
291    int     newmax;
292    char  * newbuf;
293
294    newmax = *max;
295    while( *used + len + 1 > newmax )
296    {
297        newmax += SPRINTF_BUFSIZE;
298    }
299    if( newmax > *max )
300    {
301        newbuf = realloc( *buf, newmax );
302        if( NULL == newbuf )
303        {
304            return 1;
305        }
306        *buf = newbuf;
307        *max = newmax;
308    }
309
310    if( NULL != data )
311    {
312        memcpy( *buf + *used, data, len );
313        *used += len;
314    }
315
316    return 0;
317}
318
319void
320tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... )
321{
322    va_list vl;
323    char* walk = buf;
324    const char * element = first_element;
325    va_start( vl, first_element );
326    for( ;; ) {
327        const size_t n = strlen( element );
328        memcpy( walk, element, n );
329        walk += n;
330        element = (const char*) va_arg( vl, const char* );
331        if( element == NULL )
332            break;
333        *walk++ = TR_PATH_DELIMITER;
334    }
335    *walk = '\0';
336    assert( walk-buf <= (int)buflen );
337}
338
339int
340tr_ioErrorFromErrno( void )
341{
342    switch( errno )
343    {
344        case EACCES:
345        case EROFS:
346            return TR_ERROR_IO_PERMISSIONS;
347        case ENOSPC:
348            return TR_ERROR_IO_SPACE;
349        case EMFILE:
350            return TR_ERROR_IO_FILE_TOO_BIG;
351        case EFBIG:
352            return TR_ERROR_IO_OPEN_FILES;
353        default:
354            tr_dbg( "generic i/o errno from errno: %s", strerror( errno ) );
355            return TR_ERROR_IO_OTHER;
356    }
357}
358
359char *
360tr_errorString( int code )
361{
362    switch( code )
363    {
364        case TR_OK:
365            return "No error";
366        case TR_ERROR:
367            return "Generic error";
368        case TR_ERROR_ASSERT:
369            return "Assert error";
370        case TR_ERROR_IO_PARENT:
371            return "Download folder does not exist";
372        case TR_ERROR_IO_PERMISSIONS:
373            return "Insufficient permissions";
374        case TR_ERROR_IO_SPACE:
375            return "Insufficient free space";
376        case TR_ERROR_IO_DUP_DOWNLOAD:
377            return "Already active transfer with same name and download folder";
378        case TR_ERROR_IO_FILE_TOO_BIG:
379            return "File too large";
380        case TR_ERROR_IO_OPEN_FILES:
381            return "Too many open files";
382        case TR_ERROR_IO_OTHER:
383            return "Generic I/O error";
384    }
385    return "Unknown error";
386}
387
388/****
389*****
390****/
391
392char*
393tr_strdup( const char * in )
394{
395    return tr_strndup( in, in ? strlen(in) : 0 );
396}
397
398char*
399tr_strndup( const char * in, int len )
400{
401    char * out = NULL;
402    if( in != NULL )
403    {
404        out = tr_calloc( len+1, 1 );
405        memcpy( out, in, len );
406    }
407    return out;
408}
409
410void*
411tr_calloc( size_t nmemb, size_t size )
412{
413    return nmemb && size ? calloc( nmemb, size ) : NULL;
414}
415
416void*
417tr_malloc( size_t size )
418{
419    return size ? malloc( size ) : NULL;
420}
421
422void*
423tr_malloc0( size_t size )
424{
425    void * ret = tr_malloc( size );
426    memset( ret, 0, size );
427    return ret;
428}
429
430void tr_free( void * p )
431{
432    if( p )
433        free( p );
434}
435
436/****
437*****
438****/
439
440/* note that the argument is how many bits are needed, not bytes */
441tr_bitfield_t*
442tr_bitfieldNew( size_t bitcount )
443{
444    tr_bitfield_t * ret = calloc( 1, sizeof(tr_bitfield_t) );
445    if( NULL == ret )
446        return NULL;
447
448    ret->len = ( bitcount + 7u ) / 8u;
449    ret->bits = calloc( ret->len, 1 );
450    if( NULL == ret->bits ) {
451        free( ret );
452        return NULL;
453    }
454
455    return ret;
456}
457
458tr_bitfield_t*
459tr_bitfieldDup( const tr_bitfield_t * in )
460{
461    tr_bitfield_t * ret = calloc( 1, sizeof(tr_bitfield_t) );
462    ret->len = in->len;
463    ret->bits = malloc( ret->len );
464    memcpy( ret->bits, in->bits, ret->len );
465    return ret;
466}
467
468void tr_bitfieldFree( tr_bitfield_t * bitfield )
469{
470    if( bitfield )
471    {
472        free( bitfield->bits );
473        free( bitfield );
474    }
475}
476
477void
478tr_bitfieldClear( tr_bitfield_t * bitfield )
479{
480    memset( bitfield->bits, 0, bitfield->len );
481}
482
483int
484tr_bitfieldIsEmpty( const tr_bitfield_t * bitfield )
485{
486    unsigned int i;
487
488    for( i=0; i<bitfield->len; ++i )
489        if( bitfield->bits[i] )
490            return 0;
491
492    return 1;
493}
494
495static const uint8_t bitmask[8] = { 128u, 64u, 32u, 16u, 8u, 4u, 2u, 1u };
496
497int
498tr_bitfieldHas( const tr_bitfield_t   * bitfield,
499                size_t                  bit )
500{
501    if ( bitfield == NULL ) return 0;
502    assert( bit / 8u < bitfield->len );
503    return ( bitfield->bits[ bit/8u ] & bitmask[bit%8] ) != 0;
504}
505
506void
507tr_bitfieldAdd( tr_bitfield_t  * bitfield, size_t bit )
508{
509    assert( bit / 8u < bitfield->len );
510    bitfield->bits[ bit/8u ] |= bitmask[bit%8];
511}
512
513void
514tr_bitfieldAddRange( tr_bitfield_t  * bitfield,
515                     size_t           begin,
516                     size_t           end )
517{
518    /* TODO: there are faster ways to do this */
519    unsigned int i;
520    for( i=begin; i<end; ++i )
521        tr_bitfieldAdd( bitfield, i );
522}
523
524void
525tr_bitfieldRem( tr_bitfield_t   * bitfield,
526                size_t            bit )
527{
528    if( bitfield != NULL )
529    {
530        assert( bit / 8u < bitfield->len );
531        bitfield->bits[bit/8u] &= ~bitmask[bit%8];
532    }
533}
534
535void
536tr_bitfieldRemRange ( tr_bitfield_t  * b,
537                      size_t           begin,
538                      size_t           end )
539{
540    /* TODO: there are faster ways to do this */
541    unsigned int i;
542    for( i=begin; i<end; ++i )
543        tr_bitfieldRem( b, i );
544}
545
546tr_bitfield_t*
547tr_bitfieldNegate( tr_bitfield_t * b )
548{
549    uint8_t *it;
550    const uint8_t *end;
551
552    for( it=b->bits, end=it+b->len; it!=end; ++it )
553        *it = ~*it;
554
555    return b;
556}
557
558tr_bitfield_t*
559tr_bitfieldAnd( tr_bitfield_t * a, const tr_bitfield_t * b )
560{
561    uint8_t *ait;
562    const uint8_t *aend, *bit;
563
564    assert( a->len == b->len );
565
566    for( ait=a->bits, bit=b->bits, aend=ait+a->len; ait!=aend; ++ait, ++bit )
567        *ait &= *bit;
568
569    return a;
570}
571
572size_t
573tr_bitfieldCountTrueBits( const tr_bitfield_t* b )
574{
575    size_t ret = 0;
576    const uint8_t *it, *end;
577    static const int trueBitCount[512] = {
578        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,
579        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,
580        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,
581        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,
582        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,
583        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,
584        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,
585        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,
586        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,
587        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,
588        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,
589        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,
590        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,
591        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,
592        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,
593        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
594    };
595
596    for( it=b->bits, end=it+b->len; it!=end; ++it )
597        ret += trueBitCount[*it];
598
599    return ret;
600}
601
602/***
603****
604***/
605
606uint64_t
607tr_date( void )
608{
609    struct timeval tv;
610    gettimeofday( &tv, NULL );
611    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
612}
613
614void
615tr_wait( uint64_t delay_msec )
616{
617#ifdef SYS_BEOS
618    snooze( 1000 * delay_msec );
619#else
620    usleep( 1000 * delay_msec );
621#endif
622}
Note: See TracBrowser for help on using the repository browser.