Index: Input/vorbis/configure.c
===================================================================
RCS file: /cvs/xmms/Input/vorbis/configure.c,v
retrieving revision 1.17
diff -d -u -p -r1.17 configure.c
--- Input/vorbis/configure.c	3 Oct 2002 02:05:55 -0000	1.17
+++ Input/vorbis/configure.c	28 Oct 2003 19:51:18 -0000
@@ -29,6 +29,8 @@ static GtkWidget *streaming_proxy_hbox, 
 static GtkWidget *streaming_save_dirbrowser, *streaming_save_hbox;
 static GtkWidget *title_tag_override, *title_tag_box, *title_tag_entry, *title_desc;
 static GtkWidget *rg_switch, *rg_clip_switch, *rg_booster_switch, *rg_track_gain;
+/* multiple tags */
+static GtkWidget *tg_glue_box, *tg_glue, *tg_glue_last_box, *tg_glue_last, *tg_switch;
 
 vorbis_config_t vorbis_cfg;
 
@@ -64,7 +66,12 @@ static void vorbis_configurewin_ok(GtkWi
 	g_free(vorbis_cfg.save_http_path);
 	vorbis_cfg.save_http_path = g_strdup(gtk_entry_get_text(GTK_ENTRY(streaming_save_entry)));
 	g_free(vorbis_cfg.tag_format);
-        vorbis_cfg.tag_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(title_tag_entry)));	
+        vorbis_cfg.tag_format = g_strdup(gtk_entry_get_text(GTK_ENTRY(title_tag_entry)));
+	/* multiple tags */
+	g_free(vorbis_cfg.title_glue);
+        vorbis_cfg.title_glue = g_strdup(gtk_entry_get_text(GTK_ENTRY(tg_glue)));
+	g_free(vorbis_cfg.title_glue_last);
+        vorbis_cfg.title_glue_last = g_strdup(gtk_entry_get_text(GTK_ENTRY(tg_glue_last)));
 
 	tb = GTK_TOGGLE_BUTTON(title_tag_override);
 	vorbis_cfg.tag_override = gtk_toggle_button_get_active(tb);
@@ -117,6 +124,10 @@ static void vorbis_configurewin_ok(GtkWi
 	xmms_cfg_write_boolean(cfg, "vorbis", "use_replaygain", vorbis_cfg.use_replaygain);
 	xmms_cfg_write_int(cfg, "vorbis", "replaygain_mode", vorbis_cfg.replaygain_mode);
 	xmms_cfg_write_boolean(cfg, "vorbis", "use_booster", vorbis_cfg.use_booster);
+	/* multiple tags */
+	xmms_cfg_write_boolean(cfg, "vorbis", "cat_tags", vorbis_cfg.cat_tags);
+	xmms_cfg_write_string(cfg, "vorbis", "title_glue", vorbis_cfg.title_glue);
+	xmms_cfg_write_string(cfg, "vorbis", "title_glue_last", vorbis_cfg.title_glue_last);
 	xmms_cfg_write_default_file(cfg);
 	xmms_cfg_free(cfg);
 	gtk_widget_destroy(vorbis_configurewin);
@@ -128,7 +139,7 @@ static void proxy_use_cb(GtkWidget * w, 
 
 	use_proxy = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_use));
 	use_proxy_auth = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(streaming_proxy_auth_use));
-		
+
 	gtk_widget_set_sensitive(streaming_proxy_hbox, use_proxy);
 	gtk_widget_set_sensitive(streaming_proxy_auth_use, use_proxy);
 	gtk_widget_set_sensitive(streaming_proxy_auth_hbox, use_proxy && use_proxy_auth);
@@ -189,6 +200,20 @@ static void title_tag_override_cb(GtkWid
 	override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_tag_override));
 	gtk_widget_set_sensitive(title_tag_box, override);
 	gtk_widget_set_sensitive(title_desc, override);
+	/* multiple tags */
+	gtk_widget_set_sensitive(tg_switch, override);
+	gtk_widget_set_sensitive(tg_glue_box, vorbis_cfg.cat_tags && override);
+	gtk_widget_set_sensitive(tg_glue_last_box, vorbis_cfg.cat_tags && override);
+}
+
+static void tg_switch_cb(GtkWidget * w, gpointer data)
+{
+	gboolean override;
+	override = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(title_tag_override));
+	vorbis_cfg.cat_tags = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(tg_switch));
+
+	gtk_widget_set_sensitive(tg_glue_box, vorbis_cfg.cat_tags && override);
+	gtk_widget_set_sensitive(tg_glue_last_box, vorbis_cfg.cat_tags && override);
 }
 
 static void rg_switch_cb(GtkWidget * w, gpointer data)
@@ -209,6 +234,8 @@ void vorbis_configure(void)
 	GtkWidget *title_frame, *title_tag_vbox, *title_tag_label;
 	GtkWidget *rg_frame, *rg_vbox;
 	GtkWidget *bbox, *ok, *cancel;
+	/* for multiple tags */
+	GtkWidget *tg_glue_label, *tg_glue_last_label;
 	GtkWidget *rg_type_frame, *rg_type_vbox, *rg_album_gain;
 	GtkObject *streaming_size_adj, *streaming_pre_adj;
 
@@ -387,6 +414,34 @@ void vorbis_configure(void)
 	title_desc = xmms_titlestring_descriptions("pafFetndgc", 2);
 	gtk_widget_set_sensitive(title_desc, vorbis_cfg.tag_override);
 	gtk_box_pack_start(GTK_BOX(title_tag_vbox), title_desc, FALSE, FALSE, 0);
+
+	/* multiple tags */
+	tg_switch = gtk_check_button_new_with_label(_("Concatenate multiple occurences of the same tag"));
+	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(tg_switch), vorbis_cfg.cat_tags);
+	gtk_signal_connect(GTK_OBJECT(tg_switch), "clicked", tg_switch_cb, NULL);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), tg_switch, FALSE, FALSE, 0);
+
+	tg_glue_box = gtk_hbox_new(FALSE, 5);
+	gtk_widget_set_sensitive(tg_glue_box, vorbis_cfg.cat_tags);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), tg_glue_box, FALSE, FALSE, 0);
+
+	tg_glue_label = gtk_label_new(_("Normal glue string:"));
+	gtk_box_pack_start(GTK_BOX(tg_glue_box), tg_glue_label, FALSE, FALSE, 0);
+
+	tg_glue = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(tg_glue), vorbis_cfg.title_glue);
+	gtk_box_pack_start(GTK_BOX(tg_glue_box), tg_glue, TRUE, TRUE, 0);
+
+	tg_glue_last_box = gtk_hbox_new(FALSE, 5);
+	gtk_widget_set_sensitive(tg_glue_last_box, vorbis_cfg.cat_tags);
+	gtk_box_pack_start(GTK_BOX(title_tag_vbox), tg_glue_last_box, FALSE, FALSE, 0);
+
+	tg_glue_last_label = gtk_label_new(_("Last glue string:"));
+	gtk_box_pack_start(GTK_BOX(tg_glue_last_box), tg_glue_last_label, FALSE, FALSE, 0);
+
+	tg_glue_last = gtk_entry_new();
+	gtk_entry_set_text(GTK_ENTRY(tg_glue_last), vorbis_cfg.title_glue_last);
+	gtk_box_pack_start(GTK_BOX(tg_glue_last_box), tg_glue_last, TRUE, TRUE, 0);
 
 	gtk_notebook_append_page(GTK_NOTEBOOK(notebook), title_frame, gtk_label_new(_("Title")));
 
Index: Input/vorbis/fileinfo.c
===================================================================
RCS file: /cvs/xmms/Input/vorbis/fileinfo.c,v
retrieving revision 1.9
diff -d -u -p -r1.9 fileinfo.c
--- Input/vorbis/fileinfo.c	23 Jul 2002 14:27:22 -0000	1.9
+++ Input/vorbis/fileinfo.c	28 Oct 2003 19:51:24 -0000
@@ -38,6 +38,7 @@
 
 #include "vorbis.h"
 #include "vcedit.h"
+#include "fileinfo.h"
 
 static struct vte_struct {
 	FILE *in;
@@ -61,6 +62,10 @@ static GtkWidget *genre_combo, *user_com
 static GtkWidget *description_entry, *version_entry, *isrc_entry;
 static GtkWidget *copyright_entry, *organization_entry, *location_entry;
 #endif
+#ifdef VORBIS_ADV_EDIT
+static GtkWidget *name_entry, *value_entry, *tagCList;
+static gint taglist_entry = -1;
+#endif
 static GtkWidget *rg_track_entry, *rg_album_entry, *rg_track_peak_entry, *rg_album_peak_entry;
 static GtkWidget *rg_track_label, *rg_album_label, *rg_track_peak_label, *rg_album_peak_label;
 static GtkWidget *rg_show_button;
@@ -113,6 +118,149 @@ static const gchar *vorbis_genres[] =
 	N_("Anime"), N_("JPop"), N_("Synthpop")
 };
 
+/*******************************************************************/
+#ifdef VORBIS_ADV_EDIT
+
+# ifdef ALL_VORBIS_TAGS
+#  define KNOWN_TAGS 17
+# else
+#  define KNOWN_TAGS 11
+# endif
+static int vorbis_check_std( gchar *comment, gint *found )
+{
+	gchar *standard[KNOWN_TAGS] = { "title", "artist", "album",
+			"tracknumber", "genre", "date", "comment",
+			"replaygain_track_gain", "replaygain_album_gain",
+#ifdef ALL_VORBIS_TAGS
+			"replaygain_track_peak", "replaygain_album_peak",
+			"location", "description", "version", 
+			"isrc", "organization", "copyright"
+#else
+			"replaygain_track_peak", "replaygain_album_peak"
+#endif
+	};
+
+	gint i;
+	for ( i=0; i < KNOWN_TAGS; i++ )
+		if(!g_strncasecmp( comment, standard[i], strlen( standard[i] ) )
+				&& !found[i]++ ) return 0;
+
+	return 1;
+}
+
+static void vorbis_add_comments(GtkCList *clist, vorbis_comment *vc)
+{
+	gint found_std[KNOWN_TAGS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+	gchar *temp, *name, *value;
+	gchar *buffer[2];
+	gint i, taglen;
+
+	gtk_clist_clear( clist );
+
+	for ( i=0; i < vc->comments; i++ ) {
+		gint p = 0;
+		temp = vc->user_comments[i] ;
+		taglen = vc->comment_lengths[i] ;
+		name = g_malloc( taglen );
+		while( *temp != '=' ) {
+			*name++ = *temp++;
+			p++;
+		}
+		*name = 0x00;
+		if(p) name -= p;
+		if ( !vorbis_check_std( name, found_std ) ) continue;
+		value = convert_from_utf8( ++temp );
+		buffer[0] = name;
+		buffer[1] = value;
+		gtk_clist_append(clist, buffer);
+		g_free( name ); g_free( value );
+	}
+	
+}
+
+static void comlist_selected_cb (GtkWidget *clist, gint row, gint column,
+		GdkEventButton *bevent, gpointer data)
+{
+	gchar *string;
+
+	gtk_clist_get_text (GTK_CLIST (tagCList), row, 0, &string);
+	gtk_entry_set_text (GTK_ENTRY (name_entry), string);
+
+	gtk_clist_get_text (GTK_CLIST (tagCList), row, 1, &string);
+	gtk_entry_set_text (GTK_ENTRY (value_entry), string);
+
+	taglist_entry = row;
+}
+
+static void comlist_unselected_cb (GtkWidget *clist, gint row, gint column,
+		GdkEventButton *bevent, gpointer data)
+{
+	gtk_entry_set_text (GTK_ENTRY (name_entry), "");
+	gtk_entry_set_text (GTK_ENTRY (value_entry), "");
+
+	taglist_entry = -1;
+}
+
+static void update_cb (GtkWidget *widget, gpointer data)
+{
+	gchar *buffer[2];
+
+	buffer[0] = gtk_entry_get_text ( GTK_ENTRY (name_entry) );
+	buffer[1] = gtk_entry_get_text ( GTK_ENTRY (value_entry) );
+
+	if ( (!strlen(buffer[0])) || (!strlen(buffer[1])) ) return;
+
+	if ( taglist_entry > -1 ) {
+		gtk_clist_set_text( GTK_CLIST(tagCList), taglist_entry, 0,
+				buffer[0] );
+		gtk_clist_set_text( GTK_CLIST(tagCList), taglist_entry, 1,
+				buffer[1] );
+		gtk_clist_unselect_row( GTK_CLIST(tagCList), taglist_entry, 0 );
+		taglist_entry = -1;
+	} else {
+		gtk_clist_append( GTK_CLIST(tagCList), buffer );
+	}
+}
+
+static void delete_cb (GtkWidget *widget, gpointer data)
+{
+	gtk_entry_set_text ( GTK_ENTRY (name_entry), "" );
+	gtk_entry_set_text ( GTK_ENTRY (value_entry), "" );
+
+	if ( taglist_entry > -1 ) {
+		gtk_clist_remove( GTK_CLIST(tagCList), taglist_entry );
+		taglist_entry = -1;
+	}
+}
+
+static void move_up_cb (GtkWidget *widget, gpointer drawer)
+{
+	gint row = taglist_entry;
+	GtkWidget *clist = tagCList;
+
+	if ( row > 0 ) {
+		row--;
+		gtk_clist_row_move (GTK_CLIST (clist), row + 1, row);
+		gtk_clist_select_row (GTK_CLIST (clist), row, -1);
+		taglist_entry = row;
+	}
+}
+
+static void move_down_cb (GtkWidget *widget, gpointer drawer)
+{
+	gint row = taglist_entry;
+	GtkWidget *clist = tagCList;
+
+	if ( (row > -1) && (row < (GTK_CLIST (clist)->rows - 1)) ) {
+		row++;
+		gtk_clist_row_move (GTK_CLIST (clist), row - 1, row );
+		gtk_clist_select_row (GTK_CLIST (clist), row, -1);
+		taglist_entry = row;
+	}
+}
+#endif /* VORBIS_ADV_EDIT */
+/*******************************************************************/
+
 static gchar* get_comment(vorbis_comment *vc, gchar *label)
 {
 	gchar *tag;
@@ -144,6 +292,7 @@ static char** add_tag(char **list, char 
 	g_strstrip(tag);
 	if (strlen(tag) == 0)
 		tag = NULL;
+#ifndef VORBIS_ADV_EDIT
 	/*
 	 * There can be several tags with the same label.  We clear
 	 * them all.
@@ -172,6 +321,7 @@ static char** add_tag(char **list, char 
 		else
 			ptr++;
 	}
+#endif
 	if (tag)
 	{
 		int i = 0;
@@ -222,6 +372,10 @@ static void save_cb(GtkWidget * w, gpoin
 	char **comment_list;
 	vcedit_state *state;
 	vorbis_comment *comment;
+#ifdef VORBIS_ADV_EDIT
+	gint row;
+	gchar *name_s, *value_s;
+#endif
 
 	if (!g_strncasecmp(vte.filename, "http://", 7))
 		return;
@@ -237,7 +391,11 @@ static void save_cb(GtkWidget * w, gpoin
 
 	comment = vcedit_comments(state);
 
-	comment_list = get_comment_list(comment);
+#ifdef VORBIS_ADV_EDIT
+	comment_list = g_new0(char*, 1); *comment_list = NULL;
+#else
+	comment_list = comment_list = get_comment_list(comment);
+#endif
 	
 	vorbis_comment_clear(comment);
 	
@@ -277,6 +435,13 @@ static void save_cb(GtkWidget * w, gpoin
 	comment_list = add_tag(comment_list, "organization", organization);
 	comment_list = add_tag(comment_list, "copyright", copyright);
 #endif
+#ifdef VORBIS_ADV_EDIT
+	for( row = 0; row < (GTK_CLIST (tagCList)->rows); row++ ) {
+		gtk_clist_get_text( GTK_CLIST(tagCList), row, 0, &name_s );
+		gtk_clist_get_text( GTK_CLIST(tagCList), row, 1, &value_s );
+		comment_list = add_tag(comment_list, name_s, value_s);
+	}
+#endif
 	comment_list = add_tag(comment_list, "replaygain_track_gain", rg_track_gain);
 	comment_list = add_tag(comment_list, "replaygain_album_gain", rg_album_gain);
 	comment_list = add_tag(comment_list, "replaygain_track_peak", rg_track_peak);
@@ -444,6 +609,12 @@ void vorbis_file_info_box(char *fn)
 	static GtkWidget *channel_label, *length_label, *filesize_label;
 	static GtkWidget *replaygain_label, *audiophilegain_label, *peak_label;
 	static GtkWidget *filename_entry, *tag_frame;
+#ifdef VORBIS_ADV_EDIT
+	gchar *ltitles[2] = {"Name", "Value"};
+	static GtkWidget *taglist_frame, *taglist_vbox, *scrolled_win;
+	static GtkWidget *name_hbox, *value_hbox, *bbbox;
+	static GtkWidget *update_button, *delete_button, *move_up_button, *move_down_button;
+#endif
 
 	g_free(vte.filename);
 	vte.filename = g_strdup(fn);
@@ -772,7 +943,84 @@ void vorbis_file_info_box(char *fn)
 				      GTK_JUSTIFY_LEFT);
 		gtk_box_pack_start(GTK_BOX(info_box), peak_label, FALSE,
 				   FALSE, 0);
+#ifdef VORBIS_ADV_EDIT
+		taglist_frame = gtk_frame_new(_("Non-standard comment fields:"));
+		gtk_box_pack_start(GTK_BOX(vbox), taglist_frame, FALSE,
+				   FALSE, 0);
+
+		taglist_vbox = gtk_vbox_new(FALSE, 10);
+		gtk_container_add(GTK_CONTAINER(taglist_frame), taglist_vbox);
+
+		tagCList = gtk_clist_new_with_titles (2, ltitles);
+		gtk_clist_set_column_width (GTK_CLIST (tagCList), 0, 70);
+		gtk_clist_set_column_width (GTK_CLIST (tagCList), 1, 100);
+		gtk_widget_set_usize( tagCList, -1, 80 );
+		gtk_clist_column_titles_passive( GTK_CLIST( tagCList ));
+		gtk_clist_set_selection_mode(GTK_CLIST(tagCList), GTK_SELECTION_SINGLE);
+
+		scrolled_win = gtk_scrolled_window_new(NULL, NULL);
+		gtk_container_add(GTK_CONTAINER(scrolled_win), tagCList);
+		gtk_container_border_width(GTK_CONTAINER(scrolled_win), 5);
+		gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_win),
+				GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
+		gtk_box_pack_start(GTK_BOX(taglist_vbox), scrolled_win, TRUE, TRUE, 0);
+
+		name_hbox = gtk_hbox_new(FALSE, 5);
+		gtk_box_pack_start(GTK_BOX(taglist_vbox), name_hbox, FALSE, TRUE, 0);
+
+		label = gtk_label_new( "Name: " );
+		gtk_box_pack_start (GTK_BOX (name_hbox), label, FALSE, TRUE, 0);
+
+		name_entry = gtk_entry_new () ;
+		gtk_editable_set_editable (GTK_EDITABLE (name_entry), TRUE);
+		gtk_box_pack_start (GTK_BOX (name_hbox), name_entry, TRUE, TRUE, 0) ;
+
+		value_hbox = gtk_hbox_new(FALSE, 5);
+		gtk_box_pack_start(GTK_BOX(taglist_vbox), value_hbox, FALSE, TRUE, 0);
 		
+		label = gtk_label_new ("Value: ");
+		gtk_box_pack_start (GTK_BOX (value_hbox), label, FALSE, TRUE, 0);
+
+		value_entry = gtk_entry_new() ;
+		gtk_editable_set_editable (GTK_EDITABLE (value_entry), TRUE) ;
+		gtk_box_pack_start (GTK_BOX (value_hbox), value_entry, TRUE, TRUE, 0) ;
+
+		gtk_signal_connect (GTK_OBJECT (tagCList), "select_row",
+				GTK_SIGNAL_FUNC(comlist_selected_cb), NULL);
+		gtk_signal_connect (GTK_OBJECT (tagCList), "unselect_row",
+				GTK_SIGNAL_FUNC(comlist_unselected_cb), NULL);
+
+		bbbox = gtk_hbutton_box_new(); 
+		gtk_button_box_set_layout(GTK_BUTTON_BOX(bbbox),
+					  GTK_BUTTONBOX_END);
+		gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbbox), 5);
+		gtk_box_pack_start(GTK_BOX(taglist_vbox), bbbox, FALSE, FALSE, 0);
+
+		update_button = gtk_button_new_with_label(_("Update"));
+		gtk_signal_connect(GTK_OBJECT(update_button), "clicked", 
+				   GTK_SIGNAL_FUNC(update_cb), NULL);
+		GTK_WIDGET_SET_FLAGS(update_button, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbbox), update_button, TRUE, TRUE, 0);
+		gtk_widget_grab_default(update_button);
+
+		delete_button = gtk_button_new_with_label(_("Delete"));
+		gtk_signal_connect(GTK_OBJECT(delete_button), "clicked", 
+				   GTK_SIGNAL_FUNC(delete_cb), NULL);
+		GTK_WIDGET_SET_FLAGS(delete_button, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbbox), delete_button, TRUE, TRUE, 0);
+
+		move_up_button = gtk_button_new_with_label(_("Move Up"));
+		gtk_signal_connect(GTK_OBJECT(move_up_button), "clicked", 
+				   GTK_SIGNAL_FUNC(move_up_cb), NULL);
+		GTK_WIDGET_SET_FLAGS(move_up_button, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbbox), move_up_button, TRUE, TRUE, 0);
+
+		move_down_button = gtk_button_new_with_label(_("Move Down"));
+		gtk_signal_connect(GTK_OBJECT(move_down_button), "clicked", 
+				   GTK_SIGNAL_FUNC(move_down_cb), NULL);
+		GTK_WIDGET_SET_FLAGS(move_down_button, GTK_CAN_DEFAULT);
+		gtk_box_pack_start(GTK_BOX(bbbox), move_down_button, TRUE, TRUE, 0);
+#endif
 		gtk_widget_show_all(window);
 	} else
 		gdk_window_raise(window->window);
@@ -861,6 +1109,9 @@ void vorbis_file_info_box(char *fn)
 		rg_track_peak = get_comment(comment, "rg_peak"); /* Old */
 	}
 	rg_album_peak = get_comment(comment, "replaygain_album_peak"); /* Old had no album peak */
+#ifdef VORBIS_ADV_EDIT
+	vorbis_add_comments( GTK_CLIST (tagCList), comment );
+#endif
 
 	/* ov_clear closes the file */
 	if (clear_vf)
Index: Input/vorbis/fileinfo.h
===================================================================
RCS file: /cvs/xmms/Input/vorbis/fileinfo.h,v
retrieving revision 1.2
diff -d -u -p -r1.2 fileinfo.h
--- Input/vorbis/fileinfo.h	6 Apr 2001 00:34:45 -0000	1.2
+++ Input/vorbis/fileinfo.h	28 Oct 2003 19:51:24 -0000
@@ -26,6 +26,7 @@
  * Dialog will be quite large. Not quite recommended.
  */
 /* #define ALL_VORBIS_TAGS */
+#define VORBIS_ADV_EDIT
 
 
 void vorbis_file_info_box(char *filename);
Index: Input/vorbis/vorbis.c
===================================================================
RCS file: /cvs/xmms/Input/vorbis/vorbis.c,v
retrieving revision 1.41
diff -d -u -p -r1.41 vorbis.c
--- Input/vorbis/vorbis.c	9 Sep 2003 00:45:15 -0000	1.41
+++ Input/vorbis/vorbis.c	28 Oct 2003 19:51:29 -0000
@@ -52,6 +52,8 @@
 #include "vorbis.h"
 #include "http.h"
 
+/* #define VORBISFILE_HAS_BROKEN_OV_READ_FLOAT 1 */
+
 extern vorbis_config_t vorbis_cfg;
 
 static int vorbis_check_file(char *filename);
@@ -73,6 +75,8 @@ static int ovcb_seek(void *datasource, i
 static int ovcb_close(void *datasource);
 static long ovcb_tell(void *datasource);
 
+gchar *vorbis_get_titlestring( gchar*, vorbis_comment*, TitleInput* );
+
 ov_callbacks vorbis_callbacks = 
 {
 	ovcb_read,
@@ -692,10 +696,15 @@ static gchar *vorbis_generate_title(OggV
 		/* "comment" = user comment, not "" */
 		input->comment = convert_from_utf8(vorbis_comment_query(comment, "comment", 0));
 	}
-
+/*
 	displaytitle = xmms_get_titlestring(vorbis_cfg.tag_override ?
 					    vorbis_cfg.tag_format :
 					    xmms_get_gentitle_format(), input);
+*/
+	displaytitle = vorbis_cfg.tag_override ?
+		vorbis_get_titlestring( vorbis_cfg.tag_format, comment, input ) :
+		xmms_get_titlestring( xmms_get_gentitle_format(), input );
+
 	g_free(input->track_name);
 	g_free(input->performer);
 	g_free(input->album_name);
@@ -764,6 +773,8 @@ static void vorbis_init(void)
 	vorbis_cfg.use_replaygain = FALSE;
 	vorbis_cfg.replaygain_mode = REPLAYGAIN_MODE_TRACK;
 	vorbis_cfg.use_booster = FALSE;
+	vorbis_cfg.title_glue = NULL;
+	vorbis_cfg.title_glue_last = NULL;
 
 	cfg = xmms_cfg_open_default_file();
 	xmms_cfg_read_int(cfg, "vorbis", "http_buffer_size",
@@ -796,6 +807,10 @@ static void vorbis_init(void)
 	xmms_cfg_read_boolean(cfg, "vorbis", "use_replaygain", &vorbis_cfg.use_replaygain);
 	xmms_cfg_read_int(cfg, "vorbis", "replaygain_mode", &vorbis_cfg.replaygain_mode);
 	xmms_cfg_read_boolean(cfg, "vorbis", "use_booster", &vorbis_cfg.use_booster);
+	/* for multiple tags */
+	xmms_cfg_read_boolean(cfg, "vorbis", "cat_tags", &vorbis_cfg.cat_tags);
+	xmms_cfg_read_string(cfg, "vorbis", "title_glue", &vorbis_cfg.title_glue);
+	xmms_cfg_read_string(cfg, "vorbis", "title_glue_last", &vorbis_cfg.title_glue_last);
 	xmms_cfg_free(cfg);
 }
 
@@ -845,4 +860,357 @@ static long ovcb_tell(void *datasource)
 	}
 
 	return ftell((FILE *)datasource);
+}
+
+
+/********************************************************************
+
+	my vorbis titlestring routine
+
+********************************************************************/
+
+
+#define CHECK(input, field) \
+	(((unsigned) &input->field - (unsigned) input) < input->__size)
+
+#define VS(input, field) (CHECK(input, field) ? input->field : NULL)
+#define VI(input, field) (CHECK(input, field) ? input->field : 0)
+
+gchar *vorbis_get_titlestring( gchar *fmt, vorbis_comment *comment,
+		TitleInput *input )
+{
+	gchar c, *p, *p2, *p3, outbuf[256], *string, *temp_comment;
+	gchar tag_name[256], dummy_tag[256], dummy_fmt1[256], dummy_fmt2[256];
+	gint size, numpr, onemore, siz2, comment_count, number_of_comments;
+	gint f_left, f_space, f_zero, someflag, width, precision;
+	gboolean did_output = FALSE;
+	char digits[] = "0123456789";
+	gchar convert[16];
+	gint numdigits, val, i;
+	gchar my_string[256] = "";
+
+#define PUTCH(ch)			\
+	do {				\
+		if ( size-- <= 0 )	\
+			goto done;	\
+		*p++ = (ch);		\
+	} while (0)
+
+#define PUTCH2(ch)			\
+	do {				\
+		if ( siz2-- <= 0 )	\
+			goto done;	\
+		*p2++ = (ch);		\
+	} while (0)
+
+#define LEFTPAD(num)						\
+	do {							\
+		int cnt = (num);				\
+		if ( ! f_left && cnt > 0 )			\
+			while ( cnt-- > 0 )			\
+				PUTCH(f_zero ? '0' : ' ');	\
+	} while (0)
+
+#define RIGHTPAD(num)						\
+	do {							\
+		int cnt = (num);				\
+		if ( f_left && cnt > 0 )			\
+			while ( cnt-- > 0 )			\
+				PUTCH( ' ' );			\
+	} while (0)
+
+	size = sizeof( outbuf ) - 1;
+	p = outbuf;
+
+	if( fmt == NULL ) return NULL;
+
+	while( size > 0 ) {
+
+		/* Copy until we encounter '%' */
+		while(( c = *fmt++) != '%' ) {
+			if( size-- <= 0 || c == '\0' ) goto done;
+			*p++ = c;
+		}
+
+		f_left = f_space = f_zero = 0;
+		someflag = 1;
+
+		/* parse flags (tags) */
+		while (someflag)
+		{
+			switch (*fmt)
+			{
+				case '-':
+					f_left = 1;
+					fmt++;
+					break;
+				case ' ':
+					f_space = 1;
+					fmt++;
+					break;
+				case '0':
+					f_zero = 1;
+					fmt++;
+					break;
+				default:
+					someflag = 0;
+					break;
+			}
+		}
+
+		/* Parse field width. */
+		if ((c = *fmt) >= '0' && c <= '9')
+		{
+			width = 0;
+			while ((c = *fmt++) >= '0' && c <= '9')
+			{
+				width *= 10;
+				width += c - '0';
+			}
+			fmt--;
+		}
+		else
+			width = -1;
+
+		/* Parse precision. */
+		if (*fmt == '.')
+		{
+			if ((c = *++fmt) >= '0' && c <= '9')
+			{
+				precision = 0;
+				while ((c = *fmt++) >= '0' && c <= '9')
+				{
+					precision *= 10;
+					precision += c - '0';
+				}
+				fmt--;
+			}
+			else
+				precision = -1;
+		}
+		else
+			precision = -1;
+
+		if( *fmt == '%' ) { /* print a '%' */
+			PUTCH('%');
+			goto proceed;
+		}
+
+		if( *fmt == '[' ) { /* conditional tag parsing */
+			/* find the beginning of the tagname */
+			while((( c = *fmt++ ) != '=') && (c != '\0'));
+
+			/* get the tagname to test on */
+			siz2 = sizeof( dummy_tag ) - 1; p2 = dummy_tag;
+			while(((c = *fmt++) != '=' ) && (c != '\0')) PUTCH2(c); *p2 = '\0';
+			if( c == '\0' ) fmt--;
+
+			/* go after the '?' */
+			while((( c = *fmt++ ) != '?') && (c != '\0'));
+
+			siz2 = sizeof( dummy_fmt1 ) - 1; p2 = dummy_fmt1;
+			onemore = 0;
+			/* cache what to do if the test succeeds */
+			while(( c = *fmt++ ) != '\0' ) {
+				/* more conditionals */
+				if(( c == '%' ) && ( *fmt == '[' )) ++onemore;
+				if(( c == ':' ) && !onemore ) break; /* end */
+				if(( c == ']' ) &&  onemore ) --onemore;
+				PUTCH2(c);
+			} *p2 = '\0';
+			if( c == '\0' ) fmt--;
+
+			siz2 = sizeof( dummy_fmt2 ) - 1; p2 = dummy_fmt2;
+			onemore = 0;
+			/* cache the other case */
+			while(( c = *fmt++ ) != '\0' ) {
+				/* more conditionals */
+				if(( c == '%' ) && ( *fmt == '[' )) ++onemore;
+				if(( c == ']' ) && !onemore ) break; /* end */
+				if(( c == ']' ) &&  onemore ) --onemore;
+				PUTCH2(c);
+			} *p2 = '\0';
+			if( c == '\0' ) fmt--;
+
+			/* hand it over to ourselves again (might contain even more
+			 * conditions) */
+			string = convert_from_utf8(vorbis_comment_query(comment, dummy_tag, 0));
+			if( string != NULL ) { /* tag exists */
+				p2 = vorbis_get_titlestring( dummy_fmt1, comment, input );
+			} else { /* tag doesn't exist */
+				p2 = vorbis_get_titlestring( dummy_fmt2, comment, input );
+			}
+			if( p2 != NULL ) {
+				did_output = TRUE;
+				while(( c = *p2++ ) != '\0' ) PUTCH(c);
+			}
+			goto proceed;
+		}
+
+		if( *fmt++ == '=' ) { /* a full tagname that should be displayed */
+			siz2 = sizeof( tag_name ) - 1; p2 = tag_name;
+			while(( c = *fmt++ ) != '=' ) { /* displayed tag names have to end with '=' */
+				PUTCH2(c);
+				if( c == '\0' ) break; /* end of format string */
+			}
+			if( c == '\0' ) fmt--; else *p2 = '\0';
+
+
+			number_of_comments = vorbis_comment_query_count(comment, tag_name);
+			comment_count = 0;
+			string = NULL;
+
+			if (( number_of_comments > 1 ) && (vorbis_cfg.cat_tags)) { 
+				/* Okay, we're sure we have at least 2 comments */
+
+				/* The gluewords are read from the config file.
+				 * To avoid stripping of leading and trailing whitespaces, use " or '.
+				 * The first and last character is ignored for this reason. */
+
+				siz2 = sizeof( my_string ) - 1; p2 = my_string;
+				/* Lets add the strings together */
+				for (comment_count = 0; comment_count < number_of_comments; comment_count++) {
+					temp_comment = convert_from_utf8(vorbis_comment_query(comment, tag_name, comment_count));
+
+					while(( c = *temp_comment++ ) != '\0' ) PUTCH2(c); *p2 = '\0';
+
+					if (comment_count == number_of_comments-2) {
+						p3 = vorbis_cfg.title_glue_last;
+						if(*p3++) { /* skip first character */
+							while(( c = *p3++ ) != '\0' ) PUTCH2(c);
+							*(--p2) = '\0'; /* strip last character */
+						}
+					}
+					else if (comment_count != number_of_comments-1) {
+						p3 = vorbis_cfg.title_glue;
+						if(*p3++) { /* skip first character */
+							while(( c = *p3++ ) != '\0' ) PUTCH2(c);
+							*(--p2) = '\0'; /* strip last character */
+						}
+					}
+				}
+
+				string = my_string;
+
+			} else {
+				string = convert_from_utf8(vorbis_comment_query(comment, tag_name, 0));
+			}
+
+			if ( string == NULL ) goto proceed;
+		} else {
+
+			/* Parse old format conversion. */
+			switch (c = *fmt)
+			{
+			case 'a':
+				string = VS(input, album_name);
+				goto Print_string;
+			case 'c':
+				string = VS(input, comment);
+				goto Print_string;
+			case 'd':
+				string = VS(input, date);
+				goto Print_string;
+			case 'e':
+				string = VS(input, file_ext);
+				goto Print_string;
+			case 'f':
+				string = VS(input, file_name);
+				goto Print_string;
+			case 'F':
+				string = VS(input, file_path);
+				goto Print_string;
+			case 'g':
+				string = VS(input, genre);
+				goto Print_string;
+			case 'n':
+				val = VI(input, track_number);
+				goto Print_number;
+			case 'p':
+				string = VS(input, performer);
+				goto Print_string;
+			case 'y':
+				val = VI(input, year);
+				goto Print_number;
+			case 't':
+				string = VS(input, track_name);
+
+			Print_string:
+				if ( string == NULL ) goto proceed;
+			}
+		}
+
+		did_output = TRUE;
+
+		numpr = 0;
+		if (width > 0)
+		{
+			/* Calculate printed size. */
+			numpr = strlen(string);
+			if (precision >= 0 && precision < numpr)
+				numpr = precision;
+
+			LEFTPAD(width - numpr);
+		}
+
+		/* Insert string. */
+		if (precision >= 0)
+		{
+			while (precision-- > 0 &&
+			       (c = *string++) != '\0')
+				PUTCH(c);
+		}
+		else
+		{
+			while ((c = *string++) != '\0')
+				PUTCH(c);
+		}
+
+		RIGHTPAD(width - numpr);
+		goto proceed;
+
+		Print_number:
+			if ( val == 0 )
+				break;
+			if ( c != 'N' )
+				did_output = TRUE;
+
+			/* Create reversed number string. */
+			numdigits = 0;
+			do
+			{
+				convert[numdigits++] = digits[val % 10];
+				val /= 10;
+			}
+			while (val > 0);
+
+			numpr = numdigits > precision ? numdigits : precision;
+
+			/* Insert left padding. */
+			if (!f_left && width > numpr)
+			{
+				if (f_zero)
+					numpr = width;
+				else
+					for (i = width - numpr; i-- > 0;)
+						PUTCH(' ');
+			}
+
+			/* Insert zero padding. */
+			for (i = numpr - numdigits; i-- > 0;)
+				PUTCH('0');
+
+			/* Insert number. */
+			while (numdigits > 0)
+				PUTCH(convert[--numdigits]);
+
+			RIGHTPAD(width - numpr);
+
+	proceed:
+	}
+
+done:
+	*p = '\0';
+
+	return did_output ? g_strdup( outbuf ) : NULL;
 }
Index: Input/vorbis/vorbis.h
===================================================================
RCS file: /cvs/xmms/Input/vorbis/vorbis.h,v
retrieving revision 1.8
diff -d -u -p -r1.8 vorbis.h
--- Input/vorbis/vorbis.h	23 Jul 2002 14:27:22 -0000	1.8
+++ Input/vorbis/vorbis.h	28 Oct 2003 19:51:29 -0000
@@ -24,6 +24,9 @@ typedef struct {
 	gboolean use_replaygain;
 	gint replaygain_mode;
 	gboolean use_booster;
+	gboolean cat_tags;
+	gchar *title_glue;
+	gchar *title_glue_last;
 } vorbis_config_t;
 
 enum {
