source: trunk/libtransmission/utils.c @ 2154

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