qt: playlist: use item title if available
[vlc.git] / src / text / strings.c
1 /*****************************************************************************
2  * strings.c: String related functions
3  *****************************************************************************
4  * Copyright (C) 2006 VLC authors and VideoLAN
5  * Copyright (C) 2008-2009 Rémi Denis-Courmont
6  *
7  * Authors: Antoine Cellerier <dionoea at videolan dot org>
8  *          Daniel Stranger <vlc at schmaller dot de>
9  *          Rémi Denis-Courmont
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include <vlc_common.h>
34 #include <assert.h>
35
36 /* Needed by vlc_strftime */
37 #include <time.h>
38 #include <limits.h>
39 #include <math.h>
40 #include <string.h>
41 #ifndef HAVE_STRCOLL
42 # define strcoll strcasecmp
43 #endif
44
45 /* Needed by vlc_strfplayer */
46 #include <vlc_meta.h>
47 #include <vlc_aout.h>
48 #include <vlc_memstream.h>
49
50 #include <vlc_strings.h>
51 #include <vlc_charset.h>
52 #include <vlc_arrays.h>
53 #include <vlc_player.h>
54 #include <libvlc.h>
55 #include <errno.h>
56
57 static const struct xml_entity_s
58 {
59     char    psz_entity[8];
60     char    psz_char[4];
61 } xml_entities[] = {
62     /* Important: this list has to be in alphabetical order (psz_entity-wise) */
63     { "AElig;",  "Æ" },
64     { "Aacute;", "Á" },
65     { "Acirc;",  "Â" },
66     { "Agrave;", "À" },
67     { "Aring;",  "Å" },
68     { "Atilde;", "Ã" },
69     { "Auml;",   "Ä" },
70     { "Ccedil;", "Ç" },
71     { "Dagger;", "‡" },
72     { "ETH;",    "Ð" },
73     { "Eacute;", "É" },
74     { "Ecirc;",  "Ê" },
75     { "Egrave;", "È" },
76     { "Euml;",   "Ë" },
77     { "Iacute;", "Í" },
78     { "Icirc;",  "Î" },
79     { "Igrave;", "Ì" },
80     { "Iuml;",   "Ï" },
81     { "Ntilde;", "Ñ" },
82     { "OElig;",  "Œ" },
83     { "Oacute;", "Ó" },
84     { "Ocirc;",  "Ô" },
85     { "Ograve;", "Ò" },
86     { "Oslash;", "Ø" },
87     { "Otilde;", "Õ" },
88     { "Ouml;",   "Ö" },
89     { "Scaron;", "Š" },
90     { "THORN;",  "Þ" },
91     { "Uacute;", "Ú" },
92     { "Ucirc;",  "Û" },
93     { "Ugrave;", "Ù" },
94     { "Uuml;",   "Ü" },
95     { "Yacute;", "Ý" },
96     { "Yuml;",   "Ÿ" },
97     { "aacute;", "á" },
98     { "acirc;",  "â" },
99     { "acute;",  "´" },
100     { "aelig;",  "æ" },
101     { "agrave;", "à" },
102     { "amp;",    "&" },
103     { "apos;",   "'" },
104     { "aring;",  "å" },
105     { "atilde;", "ã" },
106     { "auml;",   "ä" },
107     { "bdquo;",  "„" },
108     { "brvbar;", "¦" },
109     { "ccedil;", "ç" },
110     { "cedil;",  "¸" },
111     { "cent;",   "¢" },
112     { "circ;",   "ˆ" },
113     { "copy;",   "©" },
114     { "curren;", "¤" },
115     { "dagger;", "†" },
116     { "deg;",    "°" },
117     { "divide;", "÷" },
118     { "eacute;", "é" },
119     { "ecirc;",  "ê" },
120     { "egrave;", "è" },
121     { "eth;",    "ð" },
122     { "euml;",   "ë" },
123     { "euro;",   "€" },
124     { "frac12;", "½" },
125     { "frac14;", "¼" },
126     { "frac34;", "¾" },
127     { "gt;",     ">" },
128     { "hellip;", "…" },
129     { "iacute;", "í" },
130     { "icirc;",  "î" },
131     { "iexcl;",  "¡" },
132     { "igrave;", "ì" },
133     { "iquest;", "¿" },
134     { "iuml;",   "ï" },
135     { "laquo;",  "«" },
136     { "ldquo;",  "“" },
137     { "lsaquo;", "‹" },
138     { "lsquo;",  "‘" },
139     { "lt;",     "<" },
140     { "macr;",   "¯" },
141     { "mdash;",  "—" },
142     { "micro;",  "µ" },
143     { "middot;", "·" },
144     { "nbsp;",   "\xc2\xa0" },
145     { "ndash;",  "–" },
146     { "not;",    "¬" },
147     { "ntilde;", "ñ" },
148     { "oacute;", "ó" },
149     { "ocirc;",  "ô" },
150     { "oelig;",  "œ" },
151     { "ograve;", "ò" },
152     { "ordf;",   "ª" },
153     { "ordm;",   "º" },
154     { "oslash;", "ø" },
155     { "otilde;", "õ" },
156     { "ouml;",   "ö" },
157     { "para;",   "¶" },
158     { "permil;", "‰" },
159     { "plusmn;", "±" },
160     { "pound;",  "£" },
161     { "quot;",   "\"" },
162     { "raquo;",  "»" },
163     { "rdquo;",  "”" },
164     { "reg;",    "®" },
165     { "rsaquo;", "›" },
166     { "rsquo;",  "’" },
167     { "sbquo;",  "‚" },
168     { "scaron;", "š" },
169     { "sect;",   "§" },
170     { "shy;",    "­" },
171     { "sup1;",   "¹" },
172     { "sup2;",   "²" },
173     { "sup3;",   "³" },
174     { "szlig;",  "ß" },
175     { "thorn;",  "þ" },
176     { "tilde;",  "˜" },
177     { "times;",  "×" },
178     { "trade;",  "™" },
179     { "uacute;", "ú" },
180     { "ucirc;",  "û" },
181     { "ugrave;", "ù" },
182     { "uml;",    "¨" },
183     { "uuml;",   "ü" },
184     { "yacute;", "ý" },
185     { "yen;",    "¥" },
186     { "yuml;",   "ÿ" },
187 };
188
189 static int cmp_entity (const void *key, const void *elem)
190 {
191     const struct xml_entity_s *ent = elem;
192     const char *name = key;
193
194     return strncmp (name, ent->psz_entity, strlen (ent->psz_entity));
195 }
196
197 void vlc_xml_decode( char *psz_value )
198 {
199     char *p_pos = psz_value;
200
201     while ( *psz_value )
202     {
203         if( *psz_value == '&' )
204         {
205             if( psz_value[1] == '#' )
206             {   /* &#DDD; or &#xHHHH; Unicode code point */
207                 char *psz_end;
208                 unsigned long cp;
209
210                 if( psz_value[2] == 'x' ) /* The x must be lower-case. */
211                     cp = strtoul( psz_value + 3, &psz_end, 16 );
212                 else
213                     cp = strtoul( psz_value + 2, &psz_end, 10 );
214
215                 if( *psz_end == ';' )
216                 {
217                     psz_value = psz_end + 1;
218                     if( cp == 0 )
219                         (void)0; /* skip nulls */
220                     else
221                     if( cp <= 0x7F )
222                     {
223                         *p_pos =            cp;
224                     }
225                     else
226                     /* Unicode code point outside ASCII.
227                      * &#xxx; representation is longer than UTF-8 :) */
228                     if( cp <= 0x7FF )
229                     {
230                         *p_pos++ = 0xC0 |  (cp >>  6);
231                         *p_pos   = 0x80 |  (cp        & 0x3F);
232                     }
233                     else
234                     if( cp <= 0xFFFF )
235                     {
236                         *p_pos++ = 0xE0 |  (cp >> 12);
237                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
238                         *p_pos   = 0x80 |  (cp        & 0x3F);
239                     }
240                     else
241                     if( cp <= 0x1FFFFF ) /* Outside the BMP */
242                     {   /* Unicode stops at 10FFFF, but who cares? */
243                         *p_pos++ = 0xF0 |  (cp >> 18);
244                         *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
245                         *p_pos++ = 0x80 | ((cp >>  6) & 0x3F);
246                         *p_pos   = 0x80 |  (cp        & 0x3F);
247                     }
248                 }
249                 else
250                 {
251                     /* Invalid entity number */
252                     *p_pos = *psz_value;
253                     psz_value++;
254                 }
255             }
256             else
257             {   /* Well-known XML entity */
258                 const struct xml_entity_s *ent;
259
260                 ent = bsearch (psz_value + 1, xml_entities,
261                                ARRAY_SIZE (xml_entities),
262                                sizeof (*ent), cmp_entity);
263                 if (ent != NULL)
264                 {
265                     size_t olen = strlen (ent->psz_char);
266                     memcpy (p_pos, ent->psz_char, olen);
267                     p_pos += olen - 1;
268                     psz_value += strlen (ent->psz_entity) + 1;
269                 }
270                 else
271                 {   /* No match */
272                     *p_pos = *psz_value;
273                     psz_value++;
274                 }
275             }
276         }
277         else
278         {
279             *p_pos = *psz_value;
280             psz_value++;
281         }
282
283         p_pos++;
284     }
285
286     *p_pos = '\0';
287 }
288
289 char *vlc_xml_encode (const char *str)
290 {
291     struct vlc_memstream stream;
292     size_t n;
293     uint32_t cp;
294
295     assert(str != NULL);
296     vlc_memstream_open(&stream);
297
298     while ((n = vlc_towc (str, &cp)) != 0)
299     {
300         if (unlikely(n == (size_t)-1))
301         {
302             if (vlc_memstream_close(&stream) == 0)
303                 free(stream.ptr);
304             errno = EILSEQ;
305             return NULL;
306         }
307
308         switch (cp)
309         {
310             case '\"':
311                 vlc_memstream_puts(&stream, "&quot;");
312                 break;
313             case '&':
314                 vlc_memstream_puts(&stream, "&amp;");
315                 break;
316             case '\'':
317                 vlc_memstream_puts(&stream, "&#39;");
318                 break;
319             case '<':
320                 vlc_memstream_puts(&stream, "&lt;");
321                 break;
322             case '>':
323                 vlc_memstream_puts(&stream, "&gt;");
324                 break;
325             default:
326                 if (cp < 32) /* C0 code not allowed (except 9, 10 and 13) */
327                     break;
328                 if (cp >= 128 && cp < 160) /* C1 code encoded (except 133) */
329                 {
330                     vlc_memstream_printf(&stream, "&#%"PRIu32";", cp);
331                     break;
332                 }
333                 /* fall through */
334             case 9:
335             case 10:
336             case 13:
337             case 133:
338                 vlc_memstream_write(&stream, str, n);
339                 break;
340         }
341         str += n;
342     }
343
344     if (vlc_memstream_close(&stream))
345         return NULL;
346     return stream.ptr;
347 }
348
349 /* Hex encoding */
350 void vlc_hex_encode_binary(const void *input, size_t size, char *output)
351 {
352     const unsigned char *buffer = input;
353
354     for (size_t i = 0; i < size; i++) {
355         sprintf(&output[i * 2], "%02hhx", buffer[i]);
356     }
357 }
358
359 /* Base64 encoding */
360 char *vlc_b64_encode_binary(const void *src, size_t length)
361 {
362     static const char b64[] =
363         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
364     const unsigned char *in = src;
365     char *dst = malloc((((length + 2) / 3) * 4) + 1);
366     char *out = dst;
367
368     if (unlikely(dst == NULL))
369         return NULL;
370
371     while (length >= 3) { /* pops (up to) 3 bytes of input, push 4 bytes */
372         uint_fast32_t v = (in[0] << 16) | (in[1] << 8) | in[2];
373
374         *(out++) = b64[(v >> 18)];
375         *(out++) = b64[(v >> 12) & 0x3f];
376         *(out++) = b64[(v >>  6) & 0x3f];
377         *(out++) = b64[(v >>  0) & 0x3f];
378         in += 3;
379         length -= 3;
380     }
381
382     switch (length) {
383         case 2: {
384             uint_fast16_t v = (in[0] << 8) | in[1];
385
386             *(out++) = b64[(v >> 10)];
387             *(out++) = b64[(v >>  4) & 0x3f];
388             *(out++) = b64[(v <<  2) & 0x3f];
389             *(out++) = '=';
390             break;
391         }
392
393         case 1: {
394             uint_fast8_t v = in[0];
395
396             *(out++) = b64[(v >>  2)];
397             *(out++) = b64[(v <<  4) & 0x3f];
398             *(out++) = '=';
399             *(out++) = '=';
400             break;
401         }
402     }
403
404     *out = '\0';
405     return dst;
406 }
407
408 char *vlc_b64_encode(const char *src)
409 {
410     if (src == NULL)
411         src = "";
412     return vlc_b64_encode_binary(src, strlen(src));
413 }
414
415 /* Base64 decoding */
416 size_t vlc_b64_decode_binary_to_buffer(void *dst, size_t size,
417                                        const char *restrict src)
418 {
419     static const signed char b64[256] = {
420         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 00-0F */
421         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 10-1F */
422         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,  /* 20-2F */
423         52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,  /* 30-3F */
424         -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,  /* 40-4F */
425         15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,  /* 50-5F */
426         -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,  /* 60-6F */
427         41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,  /* 70-7F */
428         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 80-8F */
429         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* 90-9F */
430         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* A0-AF */
431         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* B0-BF */
432         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* C0-CF */
433         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* D0-DF */
434         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* E0-EF */
435         -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,  /* F0-FF */
436     };
437     const unsigned char *in = (const unsigned char *)src;
438     unsigned char *out = dst;
439     signed char prev;
440     int shift = 0;
441
442     static_assert (CHAR_BIT == 8, "Oops");
443
444     while (size > 0) {
445         const signed char cur = b64[*(in++)];
446         if (cur < 0)
447             break;
448
449         if (shift != 0) {
450             *(out++) = (prev << shift) | (cur >> (6 - shift));
451             size--;
452         }
453
454         prev = cur;
455         shift = (shift + 2) & 7;
456     }
457
458     return out - (unsigned char *)dst;
459 }
460
461 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
462 {
463     const int i_src = strlen( psz_src );
464     uint8_t   *p_dst;
465
466     *pp_dst = p_dst = malloc( i_src );
467     if( !p_dst )
468         return 0;
469     return  vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
470 }
471 char *vlc_b64_decode( const char *psz_src )
472 {
473     const int i_src = strlen( psz_src );
474     char *p_dst = malloc( i_src + 1 );
475     size_t i_dst;
476     if( !p_dst )
477         return NULL;
478
479     i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
480     p_dst[i_dst] = '\0';
481
482     return p_dst;
483 }
484
485 char *vlc_strftime( const char *tformat )
486 {
487     time_t curtime;
488     struct tm loctime;
489
490     if (strcmp (tformat, "") == 0)
491         return strdup (""); /* corner case w.r.t. strftime() return value */
492
493     /* Get the current time.  */
494     time( &curtime );
495
496     /* Convert it to local time representation.  */
497     localtime_r( &curtime, &loctime );
498     for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
499     {
500         char *str = malloc (buflen);
501         if (str == NULL)
502             return NULL;
503
504         size_t len = strftime (str, buflen, tformat, &loctime);
505         if (len > 0)
506         {
507             char *ret = realloc (str, len + 1);
508             return ret ? ret : str; /* <- this cannot fail */
509         }
510         free (str);
511     }
512     vlc_assert_unreachable ();
513 }
514
515 static void write_duration(struct vlc_memstream *stream, vlc_tick_t duration)
516 {
517     lldiv_t d;
518     long long sec;
519
520     duration /= CLOCK_FREQ;
521     d = lldiv(duration, 60);
522     sec = d.rem;
523     d = lldiv(d.quot, 60);
524     vlc_memstream_printf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec);
525 }
526
527 static int write_meta(struct vlc_memstream *stream, input_item_t *item,
528                       vlc_meta_type_t type)
529 {
530     if (item == NULL)
531         return EOF;
532
533     char *value = input_item_GetMeta(item, type);
534     if (value == NULL)
535         return EOF;
536
537     vlc_memstream_puts(stream, value);
538     free(value);
539     return 0;
540 }
541
542 char *vlc_strfplayer(vlc_player_t *player, input_item_t *item, const char *s)
543 {
544     struct vlc_memstream stream[1];
545
546     char c;
547     bool b_is_format = false;
548     bool b_empty_if_na = false;
549
550     assert(s != NULL);
551
552     if (!item && player)
553         item = vlc_player_GetCurrentMedia(player);
554
555     vlc_memstream_open(stream);
556
557     while ((c = *s) != '\0')
558     {
559         s++;
560
561         if (!b_is_format)
562         {
563             if (c == '$')
564             {
565                 b_is_format = true;
566                 b_empty_if_na = false;
567                 continue;
568             }
569
570             vlc_memstream_putc(stream, c);
571             continue;
572         }
573
574         b_is_format = false;
575
576         switch (c)
577         {
578             case 'a':
579                 write_meta(stream, item, vlc_meta_Artist);
580                 break;
581             case 'b':
582                 write_meta(stream, item, vlc_meta_Album);
583                 break;
584             case 'c':
585                 write_meta(stream, item, vlc_meta_Copyright);
586                 break;
587             case 'd':
588                 write_meta(stream, item, vlc_meta_Description);
589                 break;
590             case 'e':
591                 write_meta(stream, item, vlc_meta_EncodedBy);
592                 break;
593             case 'f':
594                 if (item != NULL)
595                 {
596                     vlc_mutex_lock(&item->lock);
597                     if (item->p_stats != NULL)
598                         vlc_memstream_printf(stream, "%"PRIi64,
599                             item->p_stats->i_displayed_pictures);
600                     else if (!b_empty_if_na)
601                         vlc_memstream_putc(stream, '-');
602                     vlc_mutex_unlock(&item->lock);
603                 }
604                 else if (!b_empty_if_na)
605                     vlc_memstream_putc(stream, '-');
606                 break;
607             case 'g':
608                 write_meta(stream, item, vlc_meta_Genre);
609                 break;
610             case 'l':
611                 write_meta(stream, item, vlc_meta_Language);
612                 break;
613             case 'n':
614                 write_meta(stream, item, vlc_meta_TrackNumber);
615                 break;
616             case 'o':
617                 write_meta(stream, item, vlc_meta_TrackTotal);
618                 break;
619             case 'p':
620                 if (item == NULL)
621                     break;
622                 {
623                     char *value = input_item_GetNowPlayingFb(item);
624                     if (value == NULL)
625                         break;
626
627                     vlc_memstream_puts(stream, value);
628                     free(value);
629                 }
630                 break;
631             case 'r':
632                 write_meta(stream, item, vlc_meta_Rating);
633                 break;
634             case 's':
635             {
636                 char *lang = NULL;
637
638                 if (player != NULL)
639                     lang = vlc_player_GetCategoryLanguage(player, SPU_ES);
640                 if (lang != NULL)
641                 {
642                     vlc_memstream_puts(stream, lang);
643                     free(lang);
644                 }
645                 else if (!b_empty_if_na)
646                     vlc_memstream_putc(stream, '-');
647                 break;
648             }
649             case 't':
650                 write_meta(stream, item, vlc_meta_Title);
651                 break;
652             case 'u':
653                 write_meta(stream, item, vlc_meta_URL);
654                 break;
655             case 'A':
656                 write_meta(stream, item, vlc_meta_Date);
657                 break;
658             case 'B':
659             {
660                 if (player)
661                 {
662                     const struct vlc_player_track *track =
663                         vlc_player_GetSelectedTrack(player, AUDIO_ES);
664                     if (track)
665                     {
666                         vlc_memstream_printf(stream, "%u",
667                                              track->fmt.i_bitrate);
668                         break;
669                     }
670                 }
671                 if (!b_empty_if_na)
672                     vlc_memstream_putc(stream, '-');
673                 break;
674             }
675             case 'C':
676                 if (player)
677                 {
678                     ssize_t chapter = vlc_player_GetSelectedChapterIdx(player);
679                     if (chapter != -1)
680                     {
681                         vlc_memstream_printf(stream, "%zd", chapter);
682                         break;
683                     }
684                 }
685                 if (!b_empty_if_na)
686                     vlc_memstream_putc(stream, '-');
687                 break;
688             case 'D':
689                 if (item != NULL)
690                     write_duration(stream, input_item_GetDuration(item));
691                 else if (!b_empty_if_na)
692                     vlc_memstream_puts(stream, "--:--:--");
693                 break;
694             case 'F':
695                 if (item != NULL)
696                 {
697                     char *uri = input_item_GetURI(item);
698                     if (uri != NULL)
699                     {
700                         vlc_memstream_puts(stream, uri);
701                         free(uri);
702                     }
703                 }
704                 break;
705             case 'I':
706                 if (player)
707                 {
708                     ssize_t title = vlc_player_GetSelectedTitleIdx(player);
709                     if (title != -1)
710                     {
711                         vlc_memstream_printf(stream, "%zd", title);
712                         break;
713                     }
714                 }
715                 if (!b_empty_if_na)
716                     vlc_memstream_putc(stream, '-');
717                 break;
718             case 'L':
719                 if (player)
720                 {
721                     vlc_tick_t length = vlc_player_GetLength(player);
722                     vlc_tick_t time = vlc_player_GetTime(player);
723                     if (length != VLC_TICK_INVALID && time != VLC_TICK_INVALID)
724                         write_duration(stream, length - time);
725                 }
726                 if (!b_empty_if_na)
727                     vlc_memstream_puts(stream, "--:--:--");
728                 break;
729             case 'N':
730                 if (item != NULL)
731                 {
732                     char *name = input_item_GetName(item);
733                     if (name != NULL)
734                     {
735                         vlc_memstream_puts(stream, name);
736                         free(name);
737                     }
738                 }
739                 break;
740             case 'O':
741             {
742                 char *lang = NULL;
743
744                 if (player != NULL)
745                     lang = vlc_player_GetCategoryLanguage(player, AUDIO_ES);
746                 if (lang != NULL)
747                 {
748                     vlc_memstream_puts(stream, lang);
749                     free(lang);
750                 }
751                 else if (!b_empty_if_na)
752                     vlc_memstream_putc(stream, '-');
753                 break;
754             }
755             case 'P':
756                 if (player)
757                 {
758                     float pos = vlc_player_GetPosition(player);
759                     if (pos >= 0)
760                     {
761                         vlc_memstream_printf(stream, "%2.1f", pos);
762                         break;
763                     }
764                 }
765                 if (!b_empty_if_na)
766                     vlc_memstream_puts(stream, "--.-%");
767                 break;
768             case 'R':
769                 if (player)
770                     vlc_memstream_printf(stream, "%.3f",
771                                          vlc_player_GetRate(player));
772                 else if (!b_empty_if_na)
773                     vlc_memstream_putc(stream, '-');
774                 break;
775             case 'S':
776                 if (player)
777                 {
778                     const struct vlc_player_track *track =
779                         vlc_player_GetSelectedTrack(player, AUDIO_ES);
780                     if (track)
781                     {
782                         div_t dr = div((track->fmt.audio.i_rate + 50) / 100, 10);
783                         vlc_memstream_printf(stream, "%d.%01d", dr.quot, dr.rem);
784                         break;
785                     }
786                 }
787                 if (!b_empty_if_na)
788                     vlc_memstream_putc(stream, '-');
789                 break;
790             case 'T':
791                 if (player)
792                 {
793                     vlc_tick_t time = vlc_player_GetTime(player);
794                     if (time != VLC_TICK_INVALID)
795                     {
796                         write_duration(stream, time);
797                         break;
798                     }
799                 }
800                 if (!b_empty_if_na)
801                     vlc_memstream_puts(stream, "--:--:--");
802                 break;
803             case 'U':
804                 write_meta(stream, item, vlc_meta_Publisher);
805                 break;
806             case 'V':
807             {
808                 float vol = 0.f;
809
810                 if (player)
811                 {
812                     audio_output_t *aout = vlc_player_aout_Hold(player);
813                     if (aout != NULL)
814                     {
815                         vol = aout_VolumeGet(aout);
816                         aout_Release(aout);
817                     }
818                 }
819                 if (vol >= 0.f)
820                     vlc_memstream_printf(stream, "%ld", lroundf(vol * 256.f));
821                 else if (!b_empty_if_na)
822                     vlc_memstream_puts(stream, "---");
823                 break;
824             }
825             case '_':
826                 vlc_memstream_putc(stream, '\n');
827                 break;
828             case 'Z':
829                 if (item == NULL)
830                     break;
831                 {
832                     char *value = input_item_GetNowPlayingFb(item);
833                     if (value != NULL)
834                     {
835                         vlc_memstream_puts(stream, value);
836                         free(value);
837                     }
838                     else
839                     {
840                         char *title = input_item_GetTitleFbName(item);
841
842                         if (write_meta(stream, item, vlc_meta_Artist) >= 0
843                             && title != NULL)
844                             vlc_memstream_puts(stream, " - ");
845
846                         if (title != NULL)
847                         {
848                             vlc_memstream_puts(stream, title);
849                             free(title);
850                         }
851                     }
852                 }
853                 break;
854             case ' ':
855                 b_empty_if_na = true;
856                 b_is_format = true;
857                 break;
858             default:
859                 vlc_memstream_putc(stream, c);
860                 break;
861         }
862     }
863
864     if (vlc_memstream_close(stream))
865         return NULL;
866     return stream->ptr;
867 }
868
869 int vlc_filenamecmp(const char *a, const char *b)
870 {
871     size_t i;
872     char ca, cb;
873
874     /* Attempt to guess if the sorting algorithm should be alphabetic
875      * (i.e. collation) or numeric:
876      * - If the first mismatching characters are not both digits,
877      *   then collation is the only option.
878      * - If one of the first mismatching characters is 0 and the other is also
879      *   a digit, the comparands are probably left-padded numerical values.
880      *   It does not matter which algorithm is used: the zero will be smaller
881      *   than non-zero either way.
882      * - Otherwise, the comparands are numerical values, and might not be
883      *   aligned (i.e. not same order of magnitude). If so, collation would
884      *   fail. So numerical comparison is performed. */
885     for (i = 0; (ca = a[i]) == (cb = b[i]); i++)
886         if (ca == '\0')
887             return 0; /* strings are exactly identical */
888
889     if ((unsigned)(ca - '0') > 9 || (unsigned)(cb - '0') > 9)
890         return strcoll(a, b);
891
892     unsigned long long ua = strtoull(a + i, NULL, 10);
893     unsigned long long ub = strtoull(b + i, NULL, 10);
894
895     /* The number may be identical in two cases:
896      * - leading zero (e.g. "012" and "12")
897      * - overflow on both sides (#ULLONG_MAX) */
898     if (ua == ub)
899         return strcoll(a, b);
900
901     return (ua > ub) ? +1 : -1;
902 }
903
904 /**
905  * Sanitize a file name.
906  *
907  * Remove forbidden, potentially forbidden and otherwise evil characters from
908  * file names. That includes slashes, and popular characters like colon
909  * (on Unix anyway).
910  *
911  * \warning This function should only be used for automatically generated
912  * file names. Do not use this on full paths, only single file names without
913  * any directory separator!
914  */
915 void filename_sanitize( char *str )
916 {
917     unsigned char c;
918
919     /* Special file names, not allowed */
920     if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
921     {
922         while( *str )
923             *(str++) = '_';
924         return;
925     }
926
927     /* On platforms not using UTF-8, VLC cannot access non-Unicode paths.
928      * Also, some file systems require Unicode file names.
929      * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
930     EnsureUTF8( str );
931
932     /* Avoid leading spaces to please Windows. */
933     while( (c = *str) != '\0' )
934     {
935         if( c != ' ' )
936             break;
937         *(str++) = '_';
938     }
939
940     char *start = str;
941
942     while( (c = *str) != '\0' )
943     {
944         /* Non-printable characters are not a good idea */
945         if( c < 32 )
946             *str = '_';
947         /* This is the list of characters not allowed by Microsoft.
948          * We also black-list them on Unix as they may be confusing, and are
949          * not supported by some file system types (notably CIFS). */
950         else if( strchr( "/:\\*\"?|<>", c ) != NULL )
951             *str = '_';
952         str++;
953     }
954
955     /* Avoid trailing spaces also to please Windows. */
956     while( str > start )
957     {
958         if( *(--str) != ' ' )
959             break;
960         *str = '_';
961     }
962 }