source: trunk/libtransmission/utils.c @ 3164

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

tweak the bitfield code to be a little faster.

  • Property svn:keywords set to Date Rev Author Id
File size: 21.7 KB
Line 
1/******************************************************************************
2 * $Id: utils.c 3164 2007-09-25 17:43:39Z 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#ifdef WIN32
39    #include <windows.h> /* for Sleep */
40#elif defined(__BEOS__)
41    #include <kernel/OS.h>
42    extern int vasprintf( char **, const char *, va_list );
43#endif
44
45#include "transmission.h"
46#include "trcompat.h"
47#include "utils.h"
48#include "platform.h"
49
50#define SPRINTF_BUFSIZE         100
51
52static tr_lock      * messageLock = NULL;
53static int            messageLevel = 0;
54static int            messageQueuing = FALSE;
55static tr_msg_list *  messageQueue = NULL;
56static tr_msg_list ** messageQueueTail = &messageQueue;
57
58void tr_msgInit( void )
59{
60    if( !messageLock )
61         messageLock = tr_lockNew( );
62}
63
64FILE*
65tr_getLog( void )
66{
67    static int initialized = FALSE;
68    static FILE * file= NULL;
69
70    if( !initialized )
71    {
72        const char * str = getenv( "TR_DEBUG_FD" );
73        int fd;
74        if( str && *str )
75            fd = atoi( str );
76        switch( fd ) {
77            case 1: file = stdout; break;
78            case 2: file = stderr; break;
79            default: file = NULL; break;
80        }
81        initialized = TRUE;
82    }
83
84    return file;
85}
86
87void
88tr_setMessageLevel( int level )
89{
90    tr_msgInit();
91    tr_lockLock( messageLock );
92    messageLevel = MAX( 0, level );
93    tr_lockUnlock( messageLock );
94}
95
96int tr_getMessageLevel( void )
97{
98    int ret;
99
100    tr_msgInit();
101    tr_lockLock( messageLock );
102    ret = messageLevel;
103    tr_lockUnlock( messageLock );
104
105    return ret;
106}
107
108void tr_setMessageQueuing( int enabled )
109{
110    tr_msgInit();
111    tr_lockLock( messageLock );
112    messageQueuing = enabled;
113    tr_lockUnlock( messageLock );
114}
115
116tr_msg_list * tr_getQueuedMessages( void )
117{
118    tr_msg_list * ret;
119
120    assert( NULL != messageLock );
121    tr_lockLock( messageLock );
122    ret = messageQueue;
123    messageQueue = NULL;
124    messageQueueTail = &messageQueue;
125    tr_lockUnlock( messageLock );
126
127    return ret;
128}
129
130void tr_freeMessageList( tr_msg_list * list )
131{
132    tr_msg_list * next;
133
134    while( NULL != list )
135    {
136        next = list->next;
137        free( list->message );
138        free( list );
139        list = next;
140    }
141}
142
143void tr_msg( int level, char * msg, ... )
144{
145    va_list       args1, args2;
146    tr_msg_list * newmsg;
147    int           len1, len2;
148    FILE        * fp;
149
150    assert( NULL != messageLock );
151    tr_lockLock( messageLock );
152
153    fp = tr_getLog( );
154
155    if( !messageLevel )
156    {
157        char * env;
158        env          = getenv( "TR_DEBUG" );
159        messageLevel = ( env ? atoi( env ) : 0 ) + 1;
160        messageLevel = MAX( 1, messageLevel );
161    }
162
163    if( messageLevel >= level )
164    {
165        va_start( args1, msg );
166        if( messageQueuing )
167        {
168            newmsg = calloc( 1, sizeof( *newmsg ) );
169            if( NULL != newmsg )
170            {
171                newmsg->level = level;
172                newmsg->when = time( NULL );
173                len1 = len2 = 0;
174                va_start( args2, msg );
175                tr_vsprintf( &newmsg->message, &len1, &len2, msg,
176                             args1, args2 );
177                va_end( args2 );
178                if( fp != NULL )
179                    fprintf( fp, "%s\n", newmsg->message );
180                if( NULL == newmsg->message )
181                {
182                    free( newmsg );
183                }
184                else
185                {
186                    *messageQueueTail = newmsg;
187                    messageQueueTail = &newmsg->next;
188                }
189            }
190        }
191        else
192        {
193            if( fp == NULL )
194                fp = stderr;
195            vfprintf( fp, msg, args1 );
196            fputc( '\n', fp );
197            fflush( fp );
198        }
199        va_end( args1 );
200    }
201
202    tr_lockUnlock( messageLock );
203}
204
205int tr_rand( int sup )
206{
207    static int init = 0;
208
209    assert( sup > 0 );
210
211    if( !init )
212    {
213        srand( tr_date() );
214        init = 1;
215    }
216    return rand() % sup;
217}
218
219/***
220****
221***/
222
223void
224tr_set_compare( const void * va, size_t aCount,
225                const void * vb, size_t bCount,
226                int compare( const void * a, const void * b ),
227                size_t elementSize,
228                tr_set_func in_a_cb,
229                tr_set_func in_b_cb,
230                tr_set_func in_both_cb,
231                void * userData )
232{
233    const uint8_t * a = (const uint8_t *) va;
234    const uint8_t * b = (const uint8_t *) vb;
235    const uint8_t * aend = a + elementSize*aCount;
236    const uint8_t * bend = b + elementSize*bCount;
237
238    while( a!=aend || b!=bend )
239    {
240        if( a==aend )
241        {
242            (*in_b_cb)( (void*)b, userData );
243            b += elementSize;
244        }
245        else if ( b==bend )
246        {
247            (*in_a_cb)( (void*)a, userData );
248            a += elementSize;
249        }
250        else
251        {
252            const int val = (*compare)( a, b );
253
254            if( !val )
255            {
256                (*in_both_cb)( (void*)a, userData );
257                a += elementSize;
258                b += elementSize;
259            }
260            else if( val < 0 )
261            {
262                (*in_a_cb)( (void*)a, userData );
263                a += elementSize;
264            }
265            else if( val > 0 )
266            {
267                (*in_b_cb)( (void*)b, userData );
268                b += elementSize;
269            }
270        }
271    }
272}
273
274/***
275****
276***/
277
278
279#if 0
280void*
281tr_memmem( const void* haystack, size_t hl,
282           const void* needle,   size_t nl)
283{
284    const char *walk, *end;
285
286    if( !nl )
287        return (void*) haystack;
288
289    if( hl < nl )
290        return NULL;
291
292    for (walk=(const char*)haystack, end=walk+hl-nl; walk!=end; ++walk)
293        if( !memcmp( walk, needle, nl ) )
294            return (void*) walk;
295
296    return NULL;
297}
298#else
299void * tr_memmem ( const void *vbig, size_t big_len,
300                   const void *vlittle, size_t little_len )
301{
302    const char *big = vbig;
303    const char *little = vlittle;
304    size_t ii, jj;
305
306    if( 0 == big_len || 0 == little_len )
307    {
308        return NULL;
309    }
310
311    for( ii = 0; ii + little_len <= big_len; ii++ )
312    {
313        for( jj = 0; jj < little_len; jj++ )
314        {
315            if( big[ii + jj] != little[jj] )
316            {
317                break;
318            }
319        }
320        if( jj == little_len )
321        {
322            return (char*)big + ii;
323        }
324    }
325
326    return NULL;
327}
328#endif
329
330/**
331***
332**/
333
334int
335tr_compareUint8 (  uint8_t a,  uint8_t b )
336{
337    if( a < b ) return -1;
338    if( a > b ) return 1;
339    return 0;
340}
341
342int
343tr_compareUint16( uint16_t a, uint16_t b )
344{
345    if( a < b ) return -1;
346    if( a > b ) return 1;
347    return 0;
348}
349
350int
351tr_compareUint32( uint32_t a, uint32_t b )
352{
353    if( a < b ) return -1;
354    if( a > b ) return 1;
355    return 0;
356}
357
358int
359tr_compareUint64( uint64_t a, uint64_t b )
360{
361    if( a < b ) return -1;
362    if( a > b ) return 1;
363    return 0;
364}
365
366/**
367***
368**/
369
370struct timeval
371timevalSec ( int seconds )
372{
373    struct timeval ret;
374    ret.tv_sec = seconds;
375    ret.tv_usec = 0;
376    return ret;
377}
378
379struct timeval
380timevalMsec ( int milliseconds )
381{
382    struct timeval ret;
383    const unsigned long microseconds = milliseconds * 1000;
384    ret.tv_sec  = microseconds / 1000000;
385    ret.tv_usec = microseconds % 1000000;
386    return ret;
387}
388
389int
390tr_mkdir( const char * path, int permissions
391#ifdef WIN32
392                                             UNUSED
393#endif
394                                                    )
395{
396#ifdef WIN32
397    return mkdir( path );
398#else
399    return mkdir( path, permissions );
400#endif
401}
402
403int
404tr_mkdirp( char * path, int permissions )
405{
406    char      * p, * pp;
407    struct stat sb;
408    int done;
409
410    p = path;
411    while( '/' == *p )
412      p++;
413    pp = p;
414    done = 0;
415    while( ( p = strchr( pp, '/' ) ) || ( p = strchr( pp, '\0' ) ) )
416    {
417        if( '\0' == *p)
418        {
419            done = 1;
420        }
421        else
422        {
423            *p = '\0';
424        }
425        if( stat( path, &sb ) )
426        {
427            /* Folder doesn't exist yet */
428            if( tr_mkdir( path, permissions ) )
429            {
430                tr_err( "Could not create directory %s (%s)", path,
431                        strerror( errno ) );
432                *p = '/';
433                return 1;
434            }
435        }
436        else if( ( sb.st_mode & S_IFMT ) != S_IFDIR )
437        {
438            /* Node exists but isn't a folder */
439            tr_err( "Remove %s, it's in the way.", path );
440            *p = '/';
441            return 1;
442        }
443        if( done )
444        {
445            break;
446        }
447        *p = '/';
448        p++;
449        pp = p;
450    }
451
452    return 0;
453}
454
455int
456tr_strncasecmp( const char * s1, const char * s2, size_t n )
457{
458    if ( !n )
459        return 0;
460
461    while( n-- != 0 && tolower( *s1 ) == tolower( *s2 ) ) {
462        if( !n || !*s1 || !*s2 )
463            break;
464        ++s1;
465        ++s2;
466    }
467
468    return tolower(*(unsigned char *) s1) - tolower(*(unsigned char *) s2);
469}
470
471int tr_sprintf( char ** buf, int * used, int * max, const char * format, ... )
472{
473    va_list ap1, ap2;
474    int     ret;
475
476    va_start( ap1, format );
477    va_start( ap2, format );
478    ret = tr_vsprintf( buf, used, max, format, ap1, ap2 );
479    va_end( ap2 );
480    va_end( ap1 );
481
482    return ret;
483}
484
485int tr_vsprintf( char ** buf, int * used, int * max, const char * fmt,
486                 va_list ap1, va_list ap2 )
487{
488    int     want;
489
490    want = vsnprintf( NULL, 0, fmt, ap1 );
491
492    if( tr_concat( buf, used, max, NULL, want ) )
493    {
494        return 1;
495    }
496    assert( *used + want + 1 <= *max );
497
498    *used += vsnprintf( *buf + *used, *max - *used, fmt, ap2 );
499
500    return 0;
501}
502
503#ifndef HAVE_ASPRINTF
504
505int
506asprintf( char ** buf, const char * format, ... )
507{
508    va_list ap;
509    int     ret;
510
511    va_start( ap, format );
512    ret = vasprintf( buf, format, ap );
513    va_end( ap );
514
515    return ret;
516}
517
518int
519vasprintf( char ** buf, const char * format, va_list ap )
520{
521    va_list ap2;
522    int     used, max;
523
524    va_copy( ap2, ap );
525
526    *buf = NULL;
527    used = 0;
528    max  = 0;
529
530    if( tr_vsprintf( buf, &used, &max, format, ap, ap2 ) )
531    {
532        free( *buf );
533        return -1;
534    }
535
536    return used;
537}
538
539#endif /* HAVE_ASPRINTF */
540
541int tr_concat( char ** buf, int * used, int * max, const char * data, int len )
542{
543    int     newmax;
544    char  * newbuf;
545
546    newmax = *max;
547    while( *used + len + 1 > newmax )
548    {
549        newmax += SPRINTF_BUFSIZE;
550    }
551    if( newmax > *max )
552    {
553        newbuf = realloc( *buf, newmax );
554        if( NULL == newbuf )
555        {
556            return 1;
557        }
558        *buf = newbuf;
559        *max = newmax;
560    }
561
562    if( NULL != data )
563    {
564        memcpy( *buf + *used, data, len );
565        *used += len;
566    }
567
568    return 0;
569}
570
571void
572tr_buildPath ( char *buf, size_t buflen, const char *first_element, ... )
573{
574    va_list vl;
575    char* walk = buf;
576    const char * element = first_element;
577
578    if( first_element == NULL )
579        return;
580
581    va_start( vl, first_element );
582    for( ;; ) {
583        const size_t n = strlen( element );
584        memcpy( walk, element, n );
585        walk += n;
586        element = (const char*) va_arg( vl, const char* );
587        if( element == NULL )
588            break;
589        *walk++ = TR_PATH_DELIMITER;
590    }
591    *walk = '\0';
592    assert( walk-buf <= (int)buflen );
593}
594
595int
596tr_ioErrorFromErrno( void )
597{
598    switch( errno )
599    {
600        case EACCES:
601        case EROFS:
602            return TR_ERROR_IO_PERMISSIONS;
603        case ENOSPC:
604            return TR_ERROR_IO_SPACE;
605        case EMFILE:
606            return TR_ERROR_IO_OPEN_FILES;
607        case EFBIG:
608            return TR_ERROR_IO_FILE_TOO_BIG;
609        default:
610            tr_dbg( "generic i/o errno from errno: %s", strerror( errno ) );
611            return TR_ERROR_IO_OTHER;
612    }
613}
614
615char *
616tr_errorString( int code )
617{
618    switch( code )
619    {
620        case TR_OK:
621            return "No error";
622        case TR_ERROR:
623            return "Generic error";
624        case TR_ERROR_ASSERT:
625            return "Assert error";
626        case TR_ERROR_IO_PARENT:
627            return "Download folder does not exist";
628        case TR_ERROR_IO_PERMISSIONS:
629            return "Insufficient permissions";
630        case TR_ERROR_IO_SPACE:
631            return "Insufficient free space";
632        case TR_ERROR_IO_DUP_DOWNLOAD:
633            return "Already active transfer with same name and download folder";
634        case TR_ERROR_IO_FILE_TOO_BIG:
635            return "File too large";
636        case TR_ERROR_IO_OPEN_FILES:
637            return "Too many open files";
638        case TR_ERROR_IO_OTHER:
639            return "Generic I/O error";
640    }
641    return "Unknown error";
642}
643
644/****
645*****
646****/
647
648char*
649tr_strdup( const char * in )
650{
651    return tr_strndup( in, in ? strlen(in) : 0 );
652}
653
654char*
655tr_strndup( const char * in, int len )
656{
657    char * out = NULL;
658
659    if( in != NULL )
660    {
661        out = tr_malloc( len+1 );
662        memcpy( out, in, len );
663        out[len] = '\0';
664    }
665    return out;
666}
667
668void*
669tr_calloc( size_t nmemb, size_t size )
670{
671    return nmemb && size ? calloc( nmemb, size ) : NULL;
672}
673
674void*
675tr_malloc( size_t size )
676{
677    return size ? malloc( size ) : NULL;
678}
679
680void*
681tr_malloc0( size_t size )
682{
683    void * ret = tr_malloc( size );
684    memset( ret, 0, size );
685    return ret;
686}
687
688void tr_free( void * p )
689{
690    if( p )
691        free( p );
692}
693
694/****
695*****
696****/
697
698/* note that the argument is how many bits are needed, not bytes */
699tr_bitfield*
700tr_bitfieldNew( size_t bitcount )
701{
702    tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) );
703    if( NULL == ret )
704        return NULL;
705
706    ret->len = (bitcount+7u) / 8u;
707    ret->bits = calloc( ret->len, 1 );
708    if( NULL == ret->bits ) {
709        free( ret );
710        return NULL;
711    }
712
713    return ret;
714}
715
716tr_bitfield*
717tr_bitfieldDup( const tr_bitfield * in )
718{
719    tr_bitfield * ret = calloc( 1, sizeof(tr_bitfield) );
720    ret->len = in->len;
721    ret->bits = malloc( ret->len );
722    memcpy( ret->bits, in->bits, ret->len );
723    return ret;
724}
725
726void tr_bitfieldFree( tr_bitfield * bitfield )
727{
728    if( bitfield )
729    {
730        free( bitfield->bits );
731        free( bitfield );
732    }
733}
734
735void
736tr_bitfieldClear( tr_bitfield * bitfield )
737{
738    memset( bitfield->bits, 0, bitfield->len );
739}
740
741int
742tr_bitfieldIsEmpty( const tr_bitfield * bitfield )
743{
744    unsigned int i;
745
746    for( i=0; i<bitfield->len; ++i )
747        if( bitfield->bits[i] )
748            return 0;
749
750    return 1;
751}
752
753int
754tr_bitfieldHas( const tr_bitfield * bitfield, size_t nth )
755{
756    static const uint8_t ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
757    return bitfield!=NULL && (bitfield->bits[nth>>3u] & ands[nth&7u] );
758}
759
760void
761tr_bitfieldAdd( tr_bitfield  * bitfield, size_t nth )
762{
763    static const uint8_t ands[8] = { 128, 64, 32, 16, 8, 4, 2, 1 };
764    bitfield->bits[nth>>3u] |= ands[nth&7u];
765}
766
767void
768tr_bitfieldAddRange( tr_bitfield  * bitfield,
769                     size_t         begin,
770                     size_t         end )
771{
772    /* TODO: there are faster ways to do this */
773    unsigned int i;
774    for( i=begin; i<end; ++i )
775        tr_bitfieldAdd( bitfield, i );
776}
777
778void
779tr_bitfieldRem( tr_bitfield   * bitfield,
780                size_t          nth )
781{
782    static const uint8_t rems[8] = { 127, 191, 223, 239, 247, 251, 253, 254 };
783
784    if( bitfield != NULL )
785        bitfield->bits[nth>>3u] &= rems[nth&7u];
786}
787
788void
789tr_bitfieldRemRange ( tr_bitfield  * b,
790                      size_t         begin,
791                      size_t         end )
792{
793    /* TODO: there are faster ways to do this */
794    unsigned int i;
795    for( i=begin; i<end; ++i )
796        tr_bitfieldRem( b, i );
797}
798
799tr_bitfield*
800tr_bitfieldNegate( tr_bitfield * b )
801{
802    uint8_t *it;
803    const uint8_t *end;
804
805    for( it=b->bits, end=it+b->len; it!=end; ++it )
806        *it = ~*it;
807
808    return b;
809}
810
811tr_bitfield*
812tr_bitfieldAnd( tr_bitfield * a, const tr_bitfield * b )
813{
814    uint8_t *ait;
815    const uint8_t *aend, *bit;
816
817    assert( a->len == b->len );
818
819    for( ait=a->bits, bit=b->bits, aend=ait+a->len; ait!=aend; ++ait, ++bit )
820        *ait &= *bit;
821
822    return a;
823}
824
825size_t
826tr_bitfieldCountTrueBits( const tr_bitfield* b )
827{
828    size_t ret = 0;
829    const uint8_t *it, *end;
830    static const int trueBitCount[512] = {
831        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,
832        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,
833        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,
834        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,
835        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,
836        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,
837        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,
838        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,
839        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,
840        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,
841        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,
842        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,
843        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,
844        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,
845        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,
846        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
847    };
848
849    if( !b )
850        return 0;
851
852    for( it=b->bits, end=it+b->len; it!=end; ++it )
853        ret += trueBitCount[*it];
854
855    return ret;
856}
857
858/***
859****
860***/
861
862uint64_t
863tr_date( void )
864{
865    struct timeval tv;
866    gettimeofday( &tv, NULL );
867    return (uint64_t) tv.tv_sec * 1000 + ( tv.tv_usec / 1000 );
868}
869
870void
871tr_wait( uint64_t delay_milliseconds )
872{
873#ifdef __BEOS__
874    snooze( 1000 * delay_milliseconds );
875#elif defined(WIN32)
876    Sleep( (DWORD)delay_milliseconds );
877#else
878    usleep( 1000 * delay_milliseconds );
879#endif
880}
881
882#define WANTBYTES( want, got ) \
883    if( (want) > (got) ) { return; } else { (got) -= (want); }
884void
885strlcat_utf8( void * dest, const void * src, size_t len, char skip )
886{
887    char       * s      = dest;
888    const char * append = src;
889    const char * p;
890
891    /* don't overwrite the nul at the end */
892    len--;
893
894    /* Go to the end of the destination string */
895    while( s[0] )
896    {
897        s++;
898        len--;
899    }
900
901    /* Now start appending, converting on the fly if necessary */
902    for( p = append; p[0]; )
903    {
904        /* skip over the requested character */
905        if( skip == p[0] )
906        {
907            p++;
908            continue;
909        }
910
911        if( !( p[0] & 0x80 ) )
912        {
913            /* ASCII character */
914            WANTBYTES( 1, len );
915            *(s++) = *(p++);
916            continue;
917        }
918
919        if( ( p[0] & 0xE0 ) == 0xC0 && ( p[1] & 0xC0 ) == 0x80 )
920        {
921            /* 2-bytes UTF-8 character */
922            WANTBYTES( 2, len );
923            *(s++) = *(p++); *(s++) = *(p++);
924            continue;
925        }
926
927        if( ( p[0] & 0xF0 ) == 0xE0 && ( p[1] & 0xC0 ) == 0x80 &&
928            ( p[2] & 0xC0 ) == 0x80 )
929        {
930            /* 3-bytes UTF-8 character */
931            WANTBYTES( 3, len );
932            *(s++) = *(p++); *(s++) = *(p++);
933            *(s++) = *(p++);
934            continue;
935        }
936
937        if( ( p[0] & 0xF8 ) == 0xF0 && ( p[1] & 0xC0 ) == 0x80 &&
938            ( p[2] & 0xC0 ) == 0x80 && ( p[3] & 0xC0 ) == 0x80 )
939        {
940            /* 4-bytes UTF-8 character */
941            WANTBYTES( 4, len );
942            *(s++) = *(p++); *(s++) = *(p++);
943            *(s++) = *(p++); *(s++) = *(p++);
944            continue;
945        }
946
947        /* ISO 8859-1 -> UTF-8 conversion */
948        WANTBYTES( 2, len );
949        *(s++) = 0xC0 | ( ( *p & 0xFF ) >> 6 );
950        *(s++) = 0x80 | ( *(p++) & 0x3F );
951    }
952}
953
954size_t
955bufsize_utf8( const void * vstr, int * changed )
956{
957    const char * str = vstr;
958    size_t       ii, grow;
959
960    if( NULL != changed )
961        *changed = 0;
962
963    ii   = 0;
964    grow = 1;
965    while( '\0' != str[ii] )
966    {
967        if( !( str[ii] & 0x80 ) )
968            /* ASCII character */
969            ii++;
970        else if( ( str[ii]   & 0xE0 ) == 0xC0 && ( str[ii+1] & 0xC0 ) == 0x80 )
971            /* 2-bytes UTF-8 character */
972            ii += 2;
973        else if( ( str[ii]   & 0xF0 ) == 0xE0 && ( str[ii+1] & 0xC0 ) == 0x80 &&
974                 ( str[ii+2] & 0xC0 ) == 0x80 )
975            /* 3-bytes UTF-8 character */
976            ii += 3;
977        else if( ( str[ii]   & 0xF8 ) == 0xF0 && ( str[ii+1] & 0xC0 ) == 0x80 &&
978                 ( str[ii+2] & 0xC0 ) == 0x80 && ( str[ii+3] & 0xC0 ) == 0x80 )
979            /* 4-bytes UTF-8 character */
980            ii += 4;
981        else
982        {
983            /* ISO 8859-1 -> UTF-8 conversion */
984            ii++;
985            grow++;
986            if( NULL != changed )
987                *changed = 1;
988        }
989    }
990
991    return ii + grow;
992}
Note: See TracBrowser for help on using the repository browser.