str_format_meta: rewrite and fix leaks due to expansion-unsafe macros
authorRémi Denis-Courmont <remi@remlab.net>
Fri, 14 Nov 2014 17:14:24 +0000 (19:14 +0200)
committerRémi Denis-Courmont <remi@remlab.net>
Fri, 14 Nov 2014 17:14:24 +0000 (19:14 +0200)
configure.ac
src/text/strings.c

index bb2c843..985c891 100644 (file)
@@ -548,7 +548,7 @@ need_libc=false
 
 dnl Check for usual libc functions
 AC_CHECK_DECLS([nanosleep],,,[#include <time.h>])
-AC_CHECK_FUNCS([daemon fcntl fstatvfs fork getenv getpwuid_r isatty lstat memalign mmap openat pread posix_fadvise posix_madvise setlocale stricmp strnicmp strptime uselocale pthread_cond_timedwait_monotonic_np pthread_condattr_setclock])
+AC_CHECK_FUNCS([daemon fcntl fstatvfs fork getenv getpwuid_r isatty lstat memalign mmap open_memstream openat pread posix_fadvise posix_madvise setlocale stricmp strnicmp strptime uselocale pthread_cond_timedwait_monotonic_np pthread_condattr_setclock])
 AC_REPLACE_FUNCS([atof atoll dirfd fdopendir flockfile fsync getdelim getpid gmtime_r lldiv localtime_r nrand48 poll posix_memalign rewind setenv strcasecmp strcasestr strdup strlcpy strndup strnlen strsep strtof strtok_r strtoll swab tdestroy strverscmp])
 AC_CHECK_FUNCS(fdatasync,,
   [AC_DEFINE(fdatasync, fsync, [Alias fdatasync() to fsync() if missing.])
index 933dd9c..d7212ea 100644 (file)
@@ -502,314 +502,305 @@ char *str_format_time( const char *tformat )
     assert (0);
 }
 
-static void format_duration (char *buf, size_t len, int64_t duration)
+static void write_duration(FILE *stream, int64_t duration)
 {
     lldiv_t d;
-    int sec;
+    long long sec;
 
     duration /= CLOCK_FREQ;
-    d = lldiv (duration, 60);
+    d = lldiv(duration, 60);
     sec = d.rem;
-    d = lldiv (d.quot, 60);
-    snprintf (buf, len, "%02lld:%02d:%02d", d.quot, (int)d.rem, sec);
+    d = lldiv(d.quot, 60);
+    fprintf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec);
 }
 
-#define INSERT_STRING( string )                                     \
-                    if( string != NULL )                            \
-                    {                                               \
-                        size_t len = strlen( string );              \
-                        dst = xrealloc( dst, i_size = i_size + len );\
-                        memcpy( (dst+d), string, len );             \
-                        d += len;                                   \
-                        free( string );                             \
-                    }
+static int write_meta(FILE *stream, input_item_t *item, vlc_meta_type_t type)
+{
+    if (item == NULL)
+        return EOF;
 
-/* same than INSERT_STRING, except that string won't be freed */
-#define INSERT_STRING_NO_FREE( string )                             \
-                    {                                               \
-                        size_t len = strlen( string );              \
-                        dst = xrealloc( dst, i_size = i_size + len );\
-                        memcpy( dst+d, string, len );               \
-                        d += len;                                   \
-                    }
-char *str_format_meta( input_thread_t *p_input, const char *s )
+    char *value = input_item_GetMeta(item, type);
+    if (value == NULL)
+        return EOF;
+
+    int ret = fputs(value, stream);
+    free(value);
+    return ret;
+}
+
+char *str_format_meta(input_thread_t *input, const char *s)
 {
-    char *dst = strdup( s );
-    if( unlikely(dst == NULL) )
+    char *str;
+    size_t len;
+#ifdef HAVE_OPEN_MEMSTREAM
+    FILE *stream = open_memstream(&str, &len);
+#else
+    FILE *stream = tmpfile();
+#endif
+    if (stream == NULL)
         return NULL;
 
-    input_item_t *p_item = p_input ? input_GetItem(p_input) : NULL;
-    size_t i_size = strlen( s ) + 1; /* +1 to store '\0' */
-    size_t d = 0;
+    input_item_t *item = (input != NULL) ? input_GetItem(input) : NULL;
 
+    char c;
     bool b_is_format = false;
     bool b_empty_if_na = false;
-    char buf[10];
 
-    while( *s )
+    while ((c = *s) != '\0')
     {
-        if( b_is_format )
+        s++;
+
+        if (!b_is_format)
         {
-            switch( *s )
+            if (c == '$')
             {
-                case 'a':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetArtist( p_item ) );
-                    break;
-                case 'b':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetAlbum( p_item ) );
-                    break;
-                case 'c':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetCopyright( p_item ) );
-                    break;
-                case 'd':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetDescription( p_item ) );
-                    break;
-                case 'e':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetEncodedBy( p_item ) );
-                    break;
-                case 'f':
-                    if( p_item && p_item->p_stats )
-                    {
-                        vlc_mutex_lock( &p_item->p_stats->lock );
-                        snprintf( buf, 10, "%"PRIi64,
-                                  p_item->p_stats->i_displayed_pictures );
-                        vlc_mutex_unlock( &p_item->p_stats->lock );
-                    }
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "-" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'g':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetGenre( p_item ) );
-                    break;
-                case 'l':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetLanguage( p_item ) );
-                    break;
-                case 'n':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetTrackNum( p_item ) );
-                    break;
-                case 'p':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetNowPlaying( p_item ) );
-                    break;
-                case 'r':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetRating( p_item ) );
-                    break;
-                case 's':
-                    {
-                        char *psz_lang = NULL;
-                        if( p_input )
-                            psz_lang = var_GetNonEmptyString( p_input, "sub-language" );
-                        if( psz_lang == NULL )
-                            psz_lang = strdup( b_empty_if_na ? "" : "-" );
-                        INSERT_STRING( psz_lang );
-                        break;
-                    }
-                case 't':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetTitle( p_item ) );
-                    break;
-                case 'u':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetURL( p_item ) );
-                    break;
-                case 'A':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetDate( p_item ) );
-                    break;
-                case 'B':
-                    if( p_input )
-                        snprintf( buf, 10, "%"PRId64,
-                                  var_GetInteger( p_input, "bit-rate" )/1000 );
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "-" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'C':
-                    if( p_input )
-                        snprintf( buf, 10, "%"PRId64,
-                                  var_GetInteger( p_input, "chapter" ) );
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "-" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'D':
-                    if( p_item )
-                    {
-                        mtime_t i_duration = input_item_GetDuration( p_item );
-                        format_duration (buf, sizeof (buf), i_duration);
-                    }
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'F':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetURI( p_item ) );
-                    break;
-                case 'I':
-                    if( p_input )
-                        snprintf( buf, 10, "%"PRId64,
-                                  var_GetInteger( p_input, "title" ) );
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "-" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'L':
-                    if( p_item && p_input )
-                    {
-                        mtime_t i_duration = input_item_GetDuration( p_item );
-                        int64_t i_time = var_GetTime( p_input, "time" );
-                        format_duration( buf, sizeof(buf),
-                                         i_duration - i_time );
-                    }
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'N':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetName( p_item ) );
-                    break;
-                case 'O':
+                b_is_format = true;
+                b_empty_if_na = false;
+                continue;
+            }
+
+            fputc(c, stream);
+            continue;
+        }
+
+        b_is_format = false;
+
+        switch (c)
+        {
+            case 'a':
+                write_meta(stream, item, vlc_meta_Artist);
+                break;
+            case 'b':
+                write_meta(stream, item, vlc_meta_Album);
+                break;
+            case 'c':
+                write_meta(stream, item, vlc_meta_Copyright);
+                break;
+            case 'd':
+                write_meta(stream, item, vlc_meta_Description);
+                break;
+            case 'e':
+                write_meta(stream, item, vlc_meta_EncodedBy);
+                break;
+            case 'f':
+                if (item != NULL && item->p_stats != NULL)
                 {
-                    char *lang = NULL;
-                    if( p_input )
-                        lang = var_GetNonEmptyString( p_input,
-                                                      "audio-language" );
-                    if( lang == NULL )
-                        lang = strdup( b_empty_if_na ? "" : "-" );
-                    INSERT_STRING( lang );
-                    break;
+                    vlc_mutex_lock(&item->p_stats->lock);
+                    fprintf(stream, "%"PRIi64,
+                            item->p_stats->i_displayed_pictures);
+                    vlc_mutex_unlock(&item->p_stats->lock);
                 }
-                case 'P':
-                    if( p_input )
-                        snprintf( buf, 10, "%2.1lf",
-                                  var_GetFloat( p_input, "position" ) * 100. );
-                    else
-                        snprintf( buf, 10, b_empty_if_na ? "" : "--.-%%" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'R':
-                    if( p_input )
-                    {
-                        float f = var_GetFloat( p_input, "rate" );
-                        snprintf( buf, 10, "%.3f", f );
-                    }
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "-" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'S':
-                    if( p_input )
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            case 'g':
+                write_meta(stream, item, vlc_meta_Genre);
+                break;
+            case 'l':
+                write_meta(stream, item, vlc_meta_Language);
+                break;
+            case 'n':
+                write_meta(stream, item, vlc_meta_TrackNumber);
+                break;
+            case 'p':
+                write_meta(stream, item, vlc_meta_NowPlaying);
+                break;
+            case 'r':
+                write_meta(stream, item, vlc_meta_Rating);
+                break;
+            case 's':
+            {
+                char *lang = NULL;
+
+                if (input != NULL)
+                    lang = var_GetNonEmptyString(input, "sub-language");
+                if (lang != NULL)
+                {
+                    fputs(lang, stream);
+                    free(lang);
+                }
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            }
+            case 't':
+                write_meta(stream, item, vlc_meta_Title);
+                break;
+            case 'u':
+                write_meta(stream, item, vlc_meta_URL);
+                break;
+            case 'A':
+                write_meta(stream, item, vlc_meta_Date);
+                break;
+            case 'B':
+                if (input != NULL)
+                    fprintf(stream, "%"PRId64,
+                            var_GetInteger(input, "bit-rate") / 1000);
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            case 'C':
+                if (input != NULL)
+                    fprintf(stream, "%"PRId64,
+                            var_GetInteger(input, "chapter"));
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            case 'D':
+                if (item != NULL)
+                    write_duration(stream, input_item_GetDuration(item));
+                else if (!b_empty_if_na)
+                    fputs("--:--:--", stream);
+                break;
+            case 'F':
+                if (item != NULL)
+                {
+                    char *uri = input_item_GetURI(item);
+                    if (uri != NULL)
                     {
-                        int r = var_GetInteger( p_input, "sample-rate" );
-                        snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
+                        fputs(uri, stream);
+                        free(uri);
                     }
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "-" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'T':
-                    if( p_input )
+                }
+                break;
+            case 'I':
+                if (input != NULL)
+                    fprintf(stream, "%"PRId64, var_GetInteger(input, "title"));
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            case 'L':
+                if (item != NULL)
+                {
+                    assert(input != NULL);
+                    write_duration(stream, input_item_GetDuration(item)
+                                   - var_GetTime(input, "time"));
+                }
+                else if (!b_empty_if_na)
+                    fputs("--:--:--", stream);
+                break;
+            case 'N':
+                if (item != NULL)
+                {
+                    char *name = input_item_GetName(item);
+                    if (name != NULL)
                     {
-                        int64_t i_time = var_GetTime( p_input, "time" );
-                        format_duration( buf, sizeof(buf), i_time );
+                        fputs(name, stream);
+                        free(name);
                     }
-                    else
-                        strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
-                    INSERT_STRING_NO_FREE( buf );
-                    break;
-                case 'U':
-                    if( p_item )
-                        INSERT_STRING( input_item_GetPublisher( p_item ) );
-                    break;
-                case 'V':
+                }
+                break;
+            case 'O':
+            {
+                char *lang = NULL;
+
+                if (input != NULL)
+                    lang = var_GetNonEmptyString(input, "audio-language");
+                if (lang != NULL)
+                {
+                    fputs(lang, stream);
+                    free(lang);
+                }
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            }
+            case 'P':
+                if (input != NULL)
+                    fprintf(stream, "%2.1f",
+                            var_GetFloat(input, "position") * 100.f);
+                else if (!b_empty_if_na)
+                    fputs("--.-%", stream);
+                break;
+            case 'R':
+                if (input != NULL)
+                    fprintf(stream, "%.3f", var_GetFloat(input, "rate"));
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            case 'S':
+                if (input != NULL)
                 {
-                    float vol = 0.f;
+                    int rate = var_GetInteger(input, "sample-rate");
+                    div_t dr = div((rate + 50) / 100, 10);
 
-                    if( p_input )
-                    {
-                        audio_output_t *aout = input_GetAout( p_input );
-                        if( aout )
-                        {
-                            vol = aout_VolumeGet( aout );
-                            vlc_object_release( aout );
-                        }
-                    }
-                    if( vol >= 0.f )
-                    {
-                        snprintf( buf, 10, "%ld", lroundf(vol * 256.f) );
-                        INSERT_STRING_NO_FREE( buf );
-                    }
-                    else
-                         INSERT_STRING_NO_FREE( "---" );
-                    break;
+                    fprintf(stream, "%d.%01d", dr.quot, dr.rem);
                 }
-                case '_':
-                    *(dst+d) = '\n';
-                    d++;
-                    break;
-                case 'Z':
-                    if( p_item )
+                else if (!b_empty_if_na)
+                    fputc('-', stream);
+                break;
+            case 'T':
+                if (input != NULL)
+                    write_duration(stream, var_GetTime(input, "time"));
+                else if (!b_empty_if_na)
+                    fputs("--:--:--", stream);
+                break;
+            case 'U':
+                write_meta(stream, item, vlc_meta_Publisher);
+                break;
+            case 'V':
+            {
+                float vol = 0.f;
+
+                if (input != NULL)
+                {
+                    audio_output_t *aout = input_GetAout(input);
+                    if (aout != NULL)
                     {
-                        char *psz_now_playing = input_item_GetNowPlaying( p_item );
-                        if( EMPTY_STR( psz_now_playing ) )
-                        {
-                            char *psz_temp = input_item_GetTitleFbName( p_item );
-                            char *psz_artist = input_item_GetArtist( p_item );
-                            if( !EMPTY_STR( psz_artist ) )
-                            {
-                                INSERT_STRING( psz_artist );
-                                if ( !EMPTY_STR( psz_temp ) )
-                                    INSERT_STRING_NO_FREE( " - " );
-                            }
-                            INSERT_STRING( psz_temp );
-                        }
-                        else
-                            INSERT_STRING( psz_now_playing );
+                        vol = aout_VolumeGet(aout);
+                        vlc_object_release(aout);
                     }
-                    break;
+                }
+                if (vol >= 0.f)
+                    fprintf(stream, "%ld", lroundf(vol * 256.f));
+                else if (!b_empty_if_na)
+                    fputs("---", stream);
+                break;
+            }
+            case '_':
+                fputc('\n', stream);
+                break;
+            case 'Z':
+                if (write_meta(stream, item, vlc_meta_NowPlaying) == EOF)
+                {
+                    char *title = input_item_GetTitleFbName(item);
 
-                case ' ':
-                    b_empty_if_na = true;
-                    break;
+                    if (write_meta(stream, item, vlc_meta_Artist) >= 0
+                     && title != NULL)
+                        fputs(" - ", stream);
 
-                default:
-                    *(dst+d) = *s;
-                    d++;
-                    break;
-            }
-            if( *s != ' ' )
-                b_is_format = false;
-        }
-        else if( *s == '$' )
-        {
-            b_is_format = true;
-            b_empty_if_na = false;
-        }
-        else
-        {
-            *(dst+d) = *s;
-            d++;
+                    if (title != NULL)
+                    {
+                        fputs(title, stream);
+                        free(title);
+                    }
+                }
+                break;
+            case ' ':
+                b_empty_if_na = true;
+                b_is_format = true;
+                break;
+            default:
+                fputc(c, stream);
+                break;
         }
-        s++;
     }
-    *(dst+d) = '\0';
 
-    return dst;
+#ifdef HAVE_OPEN_MEMSTREAM
+    return (fclose(stream) == 0) ? str : NULL;
+#else
+    len = ftell(stream);
+    if (len != (size_t)-1)
+    {
+        rewind(stream);
+        str = xmalloc(len + 1);
+        fread(str, len, 1, stream);
+        str[len] = '\0';
+    }
+    fclose(stream);
+    return str;
+#endif
 }
-#undef INSERT_STRING
-#undef INSERT_STRING_NO_FREE
 
 /**
  * Remove forbidden, potentially forbidden and otherwise evil characters from