+
+SORTFN( SORT_ARTIST, first, second )
+{
+ int i_ret = meta_sort( first, second, vlc_meta_Artist, false );
+ /* Items came from the same artist: compare the albums */
+ if( i_ret == 0 )
+ i_ret = proto_SORT_ALBUM( first, second );
+
+ return i_ret;
+}
+
+SORTFN( SORT_DESCRIPTION, first, second )
+{
+ return meta_sort( first, second, vlc_meta_Description, false );
+}
+
+SORTFN( SORT_DURATION, first, second )
+{
+ return input_item_GetDuration( first->p_input ) -
+ input_item_GetDuration( second->p_input );
+}
+
+SORTFN( SORT_GENRE, first, second )
+{
+ return meta_sort( first, second, vlc_meta_Genre, false );
+}
+
+SORTFN( SORT_ID, first, second )
+{
+ return first->i_id - second->i_id;
+}
+
+SORTFN( SORT_RATING, first, second )
+{
+ return meta_sort( first, second, vlc_meta_Rating, true );
+}
+
+SORTFN( SORT_TITLE, first, second )
+{
+ return meta_strcasecmp_title( first, second );
+}
+
+SORTFN( SORT_TITLE_NODES_FIRST, first, second )
+{
+ /* If first is a node but not second */
+ if( first->i_children == -1 && second->i_children >= 0 )
+ return -1;
+ /* If second is a node but not first */
+ else if( first->i_children >= 0 && second->i_children == -1 )
+ return 1;
+ /* Both are nodes or both are not nodes */
+ else
+ return meta_strcasecmp_title( first, second );
+}
+
+SORTFN( SORT_TITLE_NUMERIC, first, second )
+{
+ int i_ret;
+ char *psz_first = input_item_GetTitleFbName( first->p_input );
+ char *psz_second = input_item_GetTitleFbName( second->p_input );
+
+ if( psz_first && psz_second )
+ i_ret = atoi( psz_first ) - atoi( psz_second );
+ else if( !psz_first && psz_second )
+ i_ret = 1;
+ else if( psz_first && !psz_second )
+ i_ret = -1;
+ else
+ i_ret = 0;
+
+ free( psz_first );
+ free( psz_second );
+ return i_ret;
+}
+
+SORTFN( SORT_TRACK_NUMBER, first, second )
+{
+ return meta_sort( first, second, vlc_meta_TrackNumber, true );
+}
+
+SORTFN( SORT_URI, first, second )
+{
+ int i_ret;
+ char *psz_first = input_item_GetURI( first->p_input );
+ char *psz_second = input_item_GetURI( second->p_input );
+
+ if( psz_first && psz_second )
+ i_ret = strcasecmp( psz_first, psz_second );
+ else if( !psz_first && psz_second )
+ i_ret = 1;
+ else if( psz_first && !psz_second )
+ i_ret = -1;
+ else
+ i_ret = 0;
+
+ free( psz_first );
+ free( psz_second );
+ return i_ret;
+}
+
+#undef SORTFN
+
+/* Generate stubs around the proto_## sorting functions, ascending and
+ * descending both. Preprocessor magic up ahead. Brace yourself.
+ */
+
+#ifndef VLC_DEFINE_SORT_FUNCTIONS
+#error Where is VLC_DEFINE_SORT_FUNCTIONS?
+#endif
+
+#define DEF( s ) \
+ static int cmp_a_##s(const void *l,const void *r) \
+ { return proto_##s(*(const playlist_item_t *const *)l, \
+ *(const playlist_item_t *const *)r); } \
+ static int cmp_d_##s(const void *l,const void *r) \
+ { return -1*proto_##s(*(const playlist_item_t * const *)l, \
+ *(const playlist_item_t * const *)r); }
+
+ VLC_DEFINE_SORT_FUNCTIONS
+
+#undef DEF
+
+/* And populate an array with the addresses */
+
+static const sortfn_t sorting_fns[NUM_SORT_FNS][2] =
+#define DEF( a ) { cmp_a_##a, cmp_d_##a },
+{ VLC_DEFINE_SORT_FUNCTIONS };
+#undef DEF
+