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) != ' ' )