Code

screen_search: fix crash when disconnected
[ncmpc.git] / src / strfsong.c
index a146583516553754d14d4c9da03f6362de858db3..35086e85de41afe083d2f43e71f58ebd7453f7a9 100644 (file)
@@ -1,21 +1,21 @@
 /* ncmpc (Ncurses MPD Client)
- * (c) 2004-2009 The Music Player Daemon Project
+ * (c) 2004-2010 The Music Player Daemon Project
  * Project homepage: http://musicpd.org
+ *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version.
-
+ *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
-
+ *
  * You should have received a copy of the GNU General Public License along
  * with this program; if not, write to the Free Software Foundation, Inc.,
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
+ */
 
 #include "strfsong.h"
 #include "charset.h"
@@ -49,14 +49,62 @@ skip(const gchar * p)
        return p;
 }
 
+#ifndef NCMPC_MINI
+
+static char *
+concat_tag_values(const char *a, const char *b)
+{
+       return g_strconcat(a, ", ", b, NULL);
+}
+
+static char *
+song_more_tag_values(const struct mpd_song *song, enum mpd_tag_type tag,
+                    const char *first)
+{
+       const char *p = mpd_song_get_tag(song, tag, 1);
+       char *buffer, *prev;
+
+       if (p == NULL)
+               return NULL;
+
+       buffer = concat_tag_values(first, p);
+       for (unsigned i = 2; (p = mpd_song_get_tag(song, tag, i)) != NULL;
+            ++i) {
+               prev = buffer;
+               buffer = concat_tag_values(buffer, p);
+               g_free(prev);
+       }
+
+       return buffer;
+}
+
+#endif /* !NCMPC_MINI */
+
 static char *
 song_tag_locale(const struct mpd_song *song, enum mpd_tag_type tag)
 {
        const char *value = mpd_song_get_tag(song, tag, 0);
+       char *result;
+#ifndef NCMPC_MINI
+       char *all;
+#endif /* !NCMPC_MINI */
+
        if (value == NULL)
                return NULL;
 
-       return utf8_to_locale(value);
+#ifndef NCMPC_MINI
+       all = song_more_tag_values(song, tag, value);
+       if (all != NULL)
+               value = all;
+#endif /* !NCMPC_MINI */
+
+       result = utf8_to_locale(value);
+
+#ifndef NCMPC_MINI
+       g_free(all);
+#endif /* !NCMPC_MINI */
+
+       return result;
 }
 
 static gsize
@@ -70,8 +118,12 @@ _strfsong(gchar *s,
        gchar *temp;
        gsize n, length = 0;
        gboolean found = FALSE;
+       /* "missed" helps handling the case of mere literal text like
+          found==TRUE instead of found==FALSE. */
+       gboolean missed = FALSE;
+
+       s[0] = '\0';
 
-       memset(s, 0, max);
        if (song == NULL)
                return 0;
 
@@ -79,9 +131,10 @@ _strfsong(gchar *s,
                /* OR */
                if (p[0] == '|') {
                        ++p;
-                       if(!found) {
-                               memset(s, 0, max);
+                       if(missed && !found) {
+                               s[0] = '\0';
                                length = 0;
+                               missed = FALSE;
                        } else {
                                p = skip(p);
                        }
@@ -91,10 +144,11 @@ _strfsong(gchar *s,
                /* AND */
                if (p[0] == '&') {
                        ++p;
-                       if(!found) {
+                       if(missed && !found) {
                                p = skip(p);
                        } else {
                                found = FALSE;
+                               missed = FALSE;
                        }
                        continue;
                }
@@ -106,6 +160,8 @@ _strfsong(gchar *s,
                                g_strlcat(s, temp, max);
                                length = strlen(s);
                                found = TRUE;
+                       } else {
+                               missed = TRUE;
                        }
                        g_free(temp);
                        continue;
@@ -114,8 +170,8 @@ _strfsong(gchar *s,
                /* EXPRESSION END */
                if (p[0] == ']') {
                        if(last) *last = p+1;
-                       if(!found && length) {
-                               memset(s, 0, max);
+                       if(missed && !found && length) {
+                               s[0] = '\0';
                                length = 0;
                        }
                        return length;
@@ -124,6 +180,7 @@ _strfsong(gchar *s,
                /* pass-through non-escaped portions of the format string */
                if (p[0] != '#' && p[0] != '%' && length<max) {
                        s[length++] = *p;
+                       s[length] = '\0';
                        p++;
                        continue;
                }
@@ -131,6 +188,7 @@ _strfsong(gchar *s,
                /* let the escape character escape itself */
                if (p[0] == '#' && p[1] != '\0' && length<max) {
                        s[length++] = *(p+1);
+                       s[length] = '\0';
                        p+=2;
                        continue;
                }
@@ -195,12 +253,14 @@ _strfsong(gchar *s,
                if( temp == NULL) {
                        gsize templen=n;
                        /* just pass-through any unknown specifiers (including esc) */
-                       /* drop a null char in so printf stops at the end of this specifier,
-                          but put the real character back in (pseudo-const) */
                        if( length+templen > max )
                                templen = max-length;
-                       g_strlcat(s, p,max);
+                       gchar *ident = g_strndup(p, templen);
+                       g_strlcat(s, ident, max);
                        length+=templen;
+                       g_free(ident);
+
+                       missed = TRUE;
                } else {
                        gsize templen = strlen(temp);