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
7 * Authors: Antoine Cellerier <dionoea at videolan dot org>
8 * Daniel Stranger <vlc at schmaller dot de>
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.
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.
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 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
33 #include <vlc_common.h>
36 /* Needed by vlc_strftime */
42 # define strcoll strcasecmp
45 /* Needed by vlc_strfplayer */
48 #include <vlc_memstream.h>
50 #include <vlc_strings.h>
51 #include <vlc_charset.h>
52 #include <vlc_arrays.h>
53 #include <vlc_player.h>
57 static const struct xml_entity_s
62 /* Important: this list has to be in alphabetical order (psz_entity-wise) */
144 { "nbsp;", "\xc2\xa0" },
189 static int cmp_entity (const void *key
, const void *elem
)
191 const struct xml_entity_s
*ent
= elem
;
192 const char *name
= key
;
194 return strncmp (name
, ent
->psz_entity
, strlen (ent
->psz_entity
));
197 void vlc_xml_decode( char *psz_value
)
199 char *p_pos
= psz_value
;
203 if( *psz_value
== '&' )
205 if( psz_value
[1] == '#' )
206 { /* &#DDD; or &#xHHHH; Unicode code point */
210 if( psz_value
[2] == 'x' ) /* The x must be lower-case. */
211 cp
= strtoul( psz_value
+ 3, &psz_end
, 16 );
213 cp
= strtoul( psz_value
+ 2, &psz_end
, 10 );
215 if( *psz_end
== ';' )
217 psz_value
= psz_end
+ 1;
219 (void)0; /* skip nulls */
226 /* Unicode code point outside ASCII.
227 * &#xxx; representation is longer than UTF-8 :) */
230 *p_pos
++ = 0xC0 | (cp
>> 6);
231 *p_pos
= 0x80 | (cp
& 0x3F);
236 *p_pos
++ = 0xE0 | (cp
>> 12);
237 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
238 *p_pos
= 0x80 | (cp
& 0x3F);
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);
251 /* Invalid entity number */
257 { /* Well-known XML entity */
258 const struct xml_entity_s
*ent
;
260 ent
= bsearch (psz_value
+ 1, xml_entities
,
261 ARRAY_SIZE (xml_entities
),
262 sizeof (*ent
), cmp_entity
);
265 size_t olen
= strlen (ent
->psz_char
);
266 memcpy (p_pos
, ent
->psz_char
, olen
);
268 psz_value
+= strlen (ent
->psz_entity
) + 1;
289 char *vlc_xml_encode (const char *str
)
291 struct vlc_memstream stream
;
296 vlc_memstream_open(&stream
);
298 while ((n
= vlc_towc (str
, &cp
)) != 0)
300 if (unlikely(n
== (size_t)-1))
302 if (vlc_memstream_close(&stream
) == 0)
311 vlc_memstream_puts(&stream
, """);
314 vlc_memstream_puts(&stream
, "&");
317 vlc_memstream_puts(&stream
, "'");
320 vlc_memstream_puts(&stream
, "<");
323 vlc_memstream_puts(&stream
, ">");
326 if (cp
< 32) /* C0 code not allowed (except 9, 10 and 13) */
328 if (cp
>= 128 && cp
< 160) /* C1 code encoded (except 133) */
330 vlc_memstream_printf(&stream
, "&#%"PRIu32
";", cp
);
338 vlc_memstream_write(&stream
, str
, n
);
344 if (vlc_memstream_close(&stream
))
350 void vlc_hex_encode_binary(const void *input
, size_t size
, char *output
)
352 const unsigned char *buffer
= input
;
354 for (size_t i
= 0; i
< size
; i
++) {
355 sprintf(&output
[i
* 2], "%02hhx", buffer
[i
]);
359 /* Base64 encoding */
360 char *vlc_b64_encode_binary(const void *src
, size_t length
)
362 static const char b64
[] =
363 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
364 const unsigned char *in
= src
;
365 char *dst
= malloc((((length
+ 2) / 3) * 4) + 1);
368 if (unlikely(dst
== NULL
))
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];
374 *(out
++) = b64
[(v
>> 18)];
375 *(out
++) = b64
[(v
>> 12) & 0x3f];
376 *(out
++) = b64
[(v
>> 6) & 0x3f];
377 *(out
++) = b64
[(v
>> 0) & 0x3f];
384 uint_fast16_t v
= (in
[0] << 8) | in
[1];
386 *(out
++) = b64
[(v
>> 10)];
387 *(out
++) = b64
[(v
>> 4) & 0x3f];
388 *(out
++) = b64
[(v
<< 2) & 0x3f];
394 uint_fast8_t v
= in
[0];
396 *(out
++) = b64
[(v
>> 2)];
397 *(out
++) = b64
[(v
<< 4) & 0x3f];
408 char *vlc_b64_encode(const char *src
)
412 return vlc_b64_encode_binary(src
, strlen(src
));
415 /* Base64 decoding */
416 size_t vlc_b64_decode_binary_to_buffer(void *dst
, size_t size
,
417 const char *restrict src
)
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 */
437 const unsigned char *in
= (const unsigned char *)src
;
438 unsigned char *out
= dst
;
442 static_assert (CHAR_BIT
== 8, "Oops");
445 const signed char cur
= b64
[*(in
++)];
450 *(out
++) = (prev
<< shift
) | (cur
>> (6 - shift
));
455 shift
= (shift
+ 2) & 7;
458 return out
- (unsigned char *)dst
;
461 size_t vlc_b64_decode_binary( uint8_t **pp_dst
, const char *psz_src
)
463 const int i_src
= strlen( psz_src
);
466 *pp_dst
= p_dst
= malloc( i_src
);
469 return vlc_b64_decode_binary_to_buffer( p_dst
, i_src
, psz_src
);
471 char *vlc_b64_decode( const char *psz_src
)
473 const int i_src
= strlen( psz_src
);
474 char *p_dst
= malloc( i_src
+ 1 );
479 i_dst
= vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst
, i_src
, psz_src
);
485 char *vlc_strftime( const char *tformat
)
490 if (strcmp (tformat
, "") == 0)
491 return strdup (""); /* corner case w.r.t. strftime() return value */
493 /* Get the current time. */
496 /* Convert it to local time representation. */
497 localtime_r( &curtime
, &loctime
);
498 for (size_t buflen
= strlen (tformat
) + 32;; buflen
+= 32)
500 char *str
= malloc (buflen
);
504 size_t len
= strftime (str
, buflen
, tformat
, &loctime
);
507 char *ret
= realloc (str
, len
+ 1);
508 return ret
? ret
: str
; /* <- this cannot fail */
512 vlc_assert_unreachable ();
515 static void write_duration(struct vlc_memstream
*stream
, vlc_tick_t duration
)
520 duration
/= CLOCK_FREQ
;
521 d
= lldiv(duration
, 60);
523 d
= lldiv(d
.quot
, 60);
524 vlc_memstream_printf(stream
, "%02lld:%02lld:%02lld", d
.quot
, d
.rem
, sec
);
527 static int write_meta(struct vlc_memstream
*stream
, input_item_t
*item
,
528 vlc_meta_type_t type
)
533 char *value
= input_item_GetMeta(item
, type
);
537 vlc_memstream_puts(stream
, value
);
542 char *vlc_strfplayer(vlc_player_t
*player
, input_item_t
*item
, const char *s
)
544 struct vlc_memstream stream
[1];
547 bool b_is_format
= false;
548 bool b_empty_if_na
= false;
553 item
= vlc_player_GetCurrentMedia(player
);
555 vlc_memstream_open(stream
);
557 while ((c
= *s
) != '\0')
566 b_empty_if_na
= false;
570 vlc_memstream_putc(stream
, c
);
579 write_meta(stream
, item
, vlc_meta_Artist
);
582 write_meta(stream
, item
, vlc_meta_Album
);
585 write_meta(stream
, item
, vlc_meta_Copyright
);
588 write_meta(stream
, item
, vlc_meta_Description
);
591 write_meta(stream
, item
, vlc_meta_EncodedBy
);
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
);
604 else if (!b_empty_if_na
)
605 vlc_memstream_putc(stream
, '-');
608 write_meta(stream
, item
, vlc_meta_Genre
);
611 write_meta(stream
, item
, vlc_meta_Language
);
614 write_meta(stream
, item
, vlc_meta_TrackNumber
);
617 write_meta(stream
, item
, vlc_meta_TrackTotal
);
623 char *value
= input_item_GetNowPlayingFb(item
);
627 vlc_memstream_puts(stream
, value
);
632 write_meta(stream
, item
, vlc_meta_Rating
);
639 lang
= vlc_player_GetCategoryLanguage(player
, SPU_ES
);
642 vlc_memstream_puts(stream
, lang
);
645 else if (!b_empty_if_na
)
646 vlc_memstream_putc(stream
, '-');
650 write_meta(stream
, item
, vlc_meta_Title
);
653 write_meta(stream
, item
, vlc_meta_URL
);
656 write_meta(stream
, item
, vlc_meta_Date
);
662 const struct vlc_player_track
*track
=
663 vlc_player_GetSelectedTrack(player
, AUDIO_ES
);
666 vlc_memstream_printf(stream
, "%u",
667 track
->fmt
.i_bitrate
);
672 vlc_memstream_putc(stream
, '-');
678 ssize_t chapter
= vlc_player_GetSelectedChapterIdx(player
);
681 vlc_memstream_printf(stream
, "%zd", chapter
);
686 vlc_memstream_putc(stream
, '-');
690 write_duration(stream
, input_item_GetDuration(item
));
691 else if (!b_empty_if_na
)
692 vlc_memstream_puts(stream
, "--:--:--");
697 char *uri
= input_item_GetURI(item
);
700 vlc_memstream_puts(stream
, uri
);
708 ssize_t title
= vlc_player_GetSelectedTitleIdx(player
);
711 vlc_memstream_printf(stream
, "%zd", title
);
716 vlc_memstream_putc(stream
, '-');
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
);
727 vlc_memstream_puts(stream
, "--:--:--");
732 char *name
= input_item_GetName(item
);
735 vlc_memstream_puts(stream
, name
);
745 lang
= vlc_player_GetCategoryLanguage(player
, AUDIO_ES
);
748 vlc_memstream_puts(stream
, lang
);
751 else if (!b_empty_if_na
)
752 vlc_memstream_putc(stream
, '-');
758 float pos
= vlc_player_GetPosition(player
);
761 vlc_memstream_printf(stream
, "%2.1f", pos
);
766 vlc_memstream_puts(stream
, "--.-%");
770 vlc_memstream_printf(stream
, "%.3f",
771 vlc_player_GetRate(player
));
772 else if (!b_empty_if_na
)
773 vlc_memstream_putc(stream
, '-');
778 const struct vlc_player_track
*track
=
779 vlc_player_GetSelectedTrack(player
, AUDIO_ES
);
782 div_t dr
= div((track
->fmt
.audio
.i_rate
+ 50) / 100, 10);
783 vlc_memstream_printf(stream
, "%d.%01d", dr
.quot
, dr
.rem
);
788 vlc_memstream_putc(stream
, '-');
793 vlc_tick_t time
= vlc_player_GetTime(player
);
794 if (time
!= VLC_TICK_INVALID
)
796 write_duration(stream
, time
);
801 vlc_memstream_puts(stream
, "--:--:--");
804 write_meta(stream
, item
, vlc_meta_Publisher
);
812 audio_output_t
*aout
= vlc_player_aout_Hold(player
);
815 vol
= aout_VolumeGet(aout
);
820 vlc_memstream_printf(stream
, "%ld", lroundf(vol
* 256.f
));
821 else if (!b_empty_if_na
)
822 vlc_memstream_puts(stream
, "---");
826 vlc_memstream_putc(stream
, '\n');
832 char *value
= input_item_GetNowPlayingFb(item
);
835 vlc_memstream_puts(stream
, value
);
840 char *title
= input_item_GetTitleFbName(item
);
842 if (write_meta(stream
, item
, vlc_meta_Artist
) >= 0
844 vlc_memstream_puts(stream
, " - ");
848 vlc_memstream_puts(stream
, title
);
855 b_empty_if_na
= true;
859 vlc_memstream_putc(stream
, c
);
864 if (vlc_memstream_close(stream
))
869 int vlc_filenamecmp(const char *a
, const char *b
)
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
++)
887 return 0; /* strings are exactly identical */
889 if ((unsigned)(ca
- '0') > 9 || (unsigned)(cb
- '0') > 9)
890 return strcoll(a
, b
);
892 unsigned long long ua
= strtoull(a
+ i
, NULL
, 10);
893 unsigned long long ub
= strtoull(b
+ i
, NULL
, 10);
895 /* The number may be identical in two cases:
896 * - leading zero (e.g. "012" and "12")
897 * - overflow on both sides (#ULLONG_MAX) */
899 return strcoll(a
, b
);
901 return (ua
> ub
) ? +1 : -1;
905 * Sanitize a file name.
907 * Remove forbidden, potentially forbidden and otherwise evil characters from
908 * file names. That includes slashes, and popular characters like colon
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!
915 void filename_sanitize( char *str
)
919 /* Special file names, not allowed */
920 if( !strcmp( str
, "." ) || !strcmp( str
, ".." ) )
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 '_'. */
932 /* Avoid leading spaces to please Windows. */
933 while( (c
= *str
) != '\0' )
942 while( (c
= *str
) != '\0' )
944 /* Non-printable characters are not a good idea */
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
)
955 /* Avoid trailing spaces also to please Windows. */
958 if( *(--str
) != ' ' )