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
8 * Authors: Antoine Cellerier <dionoea at videolan dot org>
9 * Daniel Stranger <vlc at schmaller dot de>
10 * Rémi Denis-Courmont <rem # videolan org>
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
34 #include <vlc_common.h>
37 /* Needed by vlc_strftime */
43 # define strcoll strcasecmp
46 /* Needed by vlc_strfinput */
47 #include <vlc_input.h>
50 #include <vlc_memstream.h>
52 #include <vlc_strings.h>
53 #include <vlc_charset.h>
54 #include <vlc_arrays.h>
58 static const struct xml_entity_s
63 /* Important: this list has to be in alphabetical order (psz_entity-wise) */
145 { "nbsp;", "\xc2\xa0" },
190 static int cmp_entity (const void *key
, const void *elem
)
192 const struct xml_entity_s
*ent
= elem
;
193 const char *name
= key
;
195 return strncmp (name
, ent
->psz_entity
, strlen (ent
->psz_entity
));
198 void vlc_xml_decode( char *psz_value
)
200 char *p_pos
= psz_value
;
204 if( *psz_value
== '&' )
206 if( psz_value
[1] == '#' )
207 { /* &#DDD; or &#xHHHH; Unicode code point */
211 if( psz_value
[2] == 'x' ) /* The x must be lower-case. */
212 cp
= strtoul( psz_value
+ 3, &psz_end
, 16 );
214 cp
= strtoul( psz_value
+ 2, &psz_end
, 10 );
216 if( *psz_end
== ';' )
218 psz_value
= psz_end
+ 1;
220 (void)0; /* skip nulls */
227 /* Unicode code point outside ASCII.
228 * &#xxx; representation is longer than UTF-8 :) */
231 *p_pos
++ = 0xC0 | (cp
>> 6);
232 *p_pos
= 0x80 | (cp
& 0x3F);
237 *p_pos
++ = 0xE0 | (cp
>> 12);
238 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
239 *p_pos
= 0x80 | (cp
& 0x3F);
242 if( cp
<= 0x1FFFFF ) /* Outside the BMP */
243 { /* Unicode stops at 10FFFF, but who cares? */
244 *p_pos
++ = 0xF0 | (cp
>> 18);
245 *p_pos
++ = 0x80 | ((cp
>> 12) & 0x3F);
246 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
247 *p_pos
= 0x80 | (cp
& 0x3F);
252 /* Invalid entity number */
258 { /* Well-known XML entity */
259 const struct xml_entity_s
*ent
;
261 ent
= bsearch (psz_value
+ 1, xml_entities
,
262 ARRAY_SIZE (xml_entities
),
263 sizeof (*ent
), cmp_entity
);
266 size_t olen
= strlen (ent
->psz_char
);
267 memcpy (p_pos
, ent
->psz_char
, olen
);
269 psz_value
+= strlen (ent
->psz_entity
) + 1;
290 char *vlc_xml_encode (const char *str
)
292 struct vlc_memstream stream
;
297 vlc_memstream_open(&stream
);
299 while ((n
= vlc_towc (str
, &cp
)) != 0)
301 if (unlikely(n
== (size_t)-1))
303 if (vlc_memstream_close(&stream
) == 0)
312 vlc_memstream_puts(&stream
, """);
315 vlc_memstream_puts(&stream
, "&");
318 vlc_memstream_puts(&stream
, "'");
321 vlc_memstream_puts(&stream
, "<");
324 vlc_memstream_puts(&stream
, ">");
327 if (cp
< 32) /* C0 code not allowed (except 9, 10 and 13) */
329 if (cp
>= 128 && cp
< 160) /* C1 code encoded (except 133) */
331 vlc_memstream_printf(&stream
, "&#%"PRIu32
";", cp
);
339 vlc_memstream_write(&stream
, str
, n
);
345 if (vlc_memstream_close(&stream
))
350 /* Base64 encoding */
351 char *vlc_b64_encode_binary( const uint8_t *src
, size_t i_src
)
353 static const char b64
[] =
354 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
356 char *ret
= malloc( ( i_src
+ 4 ) * 4 / 3 );
364 /* pops (up to) 3 bytes of input, push 4 bytes */
368 v
= ((unsigned)*src
++) << 24;
369 *dst
++ = b64
[v
>> 26];
375 *dst
++ = b64
[v
>> 26];
380 v
|= *src
++ << 20; // 3/3
381 *dst
++ = ( i_src
>= 2 ) ? b64
[v
>> 26] : '='; // 3/4
385 *dst
++ = ( i_src
>= 3 ) ? b64
[v
>> 26] : '='; // 4/4
397 char *vlc_b64_encode( const char *src
)
400 return vlc_b64_encode_binary( (const uint8_t*)src
, strlen(src
) );
402 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
405 /* Base64 decoding */
406 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst
, size_t i_dst
, const char *p_src
)
408 static const int b64
[256] = {
409 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
410 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
411 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
412 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
413 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
414 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
415 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
416 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
417 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
418 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
419 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
420 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
421 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
422 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
423 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
424 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
426 uint8_t *p_start
= p_dst
;
427 uint8_t *p
= (uint8_t *)p_src
;
432 for( i_level
= 0, i_last
= 0; (size_t)( p_dst
- p_start
) < i_dst
&& *p
!= '\0'; p
++ )
434 const int c
= b64
[(unsigned int)*p
];
444 *p_dst
++ = ( i_last
<< 2 ) | ( ( c
>> 4)&0x03 );
448 *p_dst
++ = ( ( i_last
<< 4 )&0xf0 ) | ( ( c
>> 2 )&0x0f );
452 *p_dst
++ = ( ( i_last
&0x03 ) << 6 ) | c
;
458 return p_dst
- p_start
;
460 size_t vlc_b64_decode_binary( uint8_t **pp_dst
, const char *psz_src
)
462 const int i_src
= strlen( psz_src
);
465 *pp_dst
= p_dst
= malloc( i_src
);
468 return vlc_b64_decode_binary_to_buffer( p_dst
, i_src
, psz_src
);
470 char *vlc_b64_decode( const char *psz_src
)
472 const int i_src
= strlen( psz_src
);
473 char *p_dst
= malloc( i_src
+ 1 );
478 i_dst
= vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst
, i_src
, psz_src
);
484 char *vlc_strftime( const char *tformat
)
489 if (strcmp (tformat
, "") == 0)
490 return strdup (""); /* corner case w.r.t. strftime() return value */
492 /* Get the current time. */
495 /* Convert it to local time representation. */
496 localtime_r( &curtime
, &loctime
);
497 for (size_t buflen
= strlen (tformat
) + 32;; buflen
+= 32)
499 char *str
= malloc (buflen
);
503 size_t len
= strftime (str
, buflen
, tformat
, &loctime
);
506 char *ret
= realloc (str
, len
+ 1);
507 return ret
? ret
: str
; /* <- this cannot fail */
511 vlc_assert_unreachable ();
514 static void write_duration(struct vlc_memstream
*stream
, int64_t duration
)
519 duration
/= CLOCK_FREQ
;
520 d
= lldiv(duration
, 60);
522 d
= lldiv(d
.quot
, 60);
523 vlc_memstream_printf(stream
, "%02lld:%02lld:%02lld", d
.quot
, d
.rem
, sec
);
526 static int write_meta(struct vlc_memstream
*stream
, input_item_t
*item
,
527 vlc_meta_type_t type
)
532 char *value
= input_item_GetMeta(item
, type
);
536 vlc_memstream_puts(stream
, value
);
541 char *vlc_strfinput(input_thread_t
*input
, const char *s
)
543 struct vlc_memstream stream
[1];
545 input_item_t
*item
= (input
!= NULL
) ? input_GetItem(input
) : NULL
;
548 bool b_is_format
= false;
549 bool b_empty_if_na
= false;
553 vlc_memstream_open(stream
);
555 while ((c
= *s
) != '\0')
564 b_empty_if_na
= false;
568 vlc_memstream_putc(stream
, c
);
577 write_meta(stream
, item
, vlc_meta_Artist
);
580 write_meta(stream
, item
, vlc_meta_Album
);
583 write_meta(stream
, item
, vlc_meta_Copyright
);
586 write_meta(stream
, item
, vlc_meta_Description
);
589 write_meta(stream
, item
, vlc_meta_EncodedBy
);
592 if (item
!= NULL
&& item
->p_stats
!= NULL
)
594 vlc_mutex_lock(&item
->p_stats
->lock
);
595 vlc_memstream_printf(stream
, "%"PRIi64
,
596 item
->p_stats
->i_displayed_pictures
);
597 vlc_mutex_unlock(&item
->p_stats
->lock
);
599 else if (!b_empty_if_na
)
600 vlc_memstream_putc(stream
, '-');
603 write_meta(stream
, item
, vlc_meta_Genre
);
606 write_meta(stream
, item
, vlc_meta_Language
);
609 write_meta(stream
, item
, vlc_meta_TrackNumber
);
612 write_meta(stream
, item
, vlc_meta_TrackTotal
);
618 char *value
= input_item_GetNowPlayingFb(item
);
622 vlc_memstream_puts(stream
, value
);
627 write_meta(stream
, item
, vlc_meta_Rating
);
634 lang
= var_GetNonEmptyString(input
, "sub-language");
637 vlc_memstream_puts(stream
, lang
);
640 else if (!b_empty_if_na
)
641 vlc_memstream_putc(stream
, '-');
645 write_meta(stream
, item
, vlc_meta_Title
);
648 write_meta(stream
, item
, vlc_meta_URL
);
651 write_meta(stream
, item
, vlc_meta_Date
);
655 vlc_memstream_printf(stream
, "%"PRId64
,
656 var_GetInteger(input
, "bit-rate") / 1000);
657 else if (!b_empty_if_na
)
658 vlc_memstream_putc(stream
, '-');
662 vlc_memstream_printf(stream
, "%"PRId64
,
663 var_GetInteger(input
, "chapter"));
664 else if (!b_empty_if_na
)
665 vlc_memstream_putc(stream
, '-');
669 write_duration(stream
, input_item_GetDuration(item
));
670 else if (!b_empty_if_na
)
671 vlc_memstream_puts(stream
, "--:--:--");
676 char *uri
= input_item_GetURI(item
);
679 vlc_memstream_puts(stream
, uri
);
686 vlc_memstream_printf(stream
, "%"PRId64
,
687 var_GetInteger(input
, "title"));
688 else if (!b_empty_if_na
)
689 vlc_memstream_putc(stream
, '-');
694 assert(input
!= NULL
);
695 write_duration(stream
, input_item_GetDuration(item
)
696 - var_GetInteger(input
, "time"));
698 else if (!b_empty_if_na
)
699 vlc_memstream_puts(stream
, "--:--:--");
704 char *name
= input_item_GetName(item
);
707 vlc_memstream_puts(stream
, name
);
717 lang
= var_GetNonEmptyString(input
, "audio-language");
720 vlc_memstream_puts(stream
, lang
);
723 else if (!b_empty_if_na
)
724 vlc_memstream_putc(stream
, '-');
729 vlc_memstream_printf(stream
, "%2.1f",
730 var_GetFloat(input
, "position") * 100.f
);
731 else if (!b_empty_if_na
)
732 vlc_memstream_puts(stream
, "--.-%");
736 vlc_memstream_printf(stream
, "%.3f",
737 var_GetFloat(input
, "rate"));
738 else if (!b_empty_if_na
)
739 vlc_memstream_putc(stream
, '-');
744 int rate
= var_GetInteger(input
, "sample-rate");
745 div_t dr
= div((rate
+ 50) / 100, 10);
747 vlc_memstream_printf(stream
, "%d.%01d", dr
.quot
, dr
.rem
);
749 else if (!b_empty_if_na
)
750 vlc_memstream_putc(stream
, '-');
754 write_duration(stream
, var_GetInteger(input
, "time"));
755 else if (!b_empty_if_na
)
756 vlc_memstream_puts(stream
, "--:--:--");
759 write_meta(stream
, item
, vlc_meta_Publisher
);
767 audio_output_t
*aout
= input_GetAout(input
);
770 vol
= aout_VolumeGet(aout
);
771 vlc_object_release(aout
);
775 vlc_memstream_printf(stream
, "%ld", lroundf(vol
* 256.f
));
776 else if (!b_empty_if_na
)
777 vlc_memstream_puts(stream
, "---");
781 vlc_memstream_putc(stream
, '\n');
787 char *value
= input_item_GetNowPlayingFb(item
);
790 vlc_memstream_puts(stream
, value
);
795 char *title
= input_item_GetTitleFbName(item
);
797 if (write_meta(stream
, item
, vlc_meta_Artist
) >= 0
799 vlc_memstream_puts(stream
, " - ");
803 vlc_memstream_puts(stream
, title
);
810 b_empty_if_na
= true;
814 vlc_memstream_putc(stream
, c
);
819 if (vlc_memstream_close(stream
))
824 int vlc_filenamecmp(const char *a
, const char *b
)
829 /* Attempt to guess if the sorting algorithm should be alphabetic
830 * (i.e. collation) or numeric:
831 * - If the first mismatching characters are not both digits,
832 * then collation is the only option.
833 * - If one of the first mismatching characters is 0 and the other is also
834 * a digit, the comparands are probably left-padded numerical values.
835 * It does not matter which algorithm is used: the zero will be smaller
836 * than non-zero either way.
837 * - Otherwise, the comparands are numerical values, and might not be
838 * aligned (i.e. not same order of magnitude). If so, collation would
839 * fail. So numerical comparison is performed. */
840 for (i
= 0; (ca
= a
[i
]) == (cb
= b
[i
]); i
++)
842 return 0; /* strings are exactly identical */
844 if ((unsigned)(ca
- '0') > 9 || (unsigned)(cb
- '0') > 9)
845 return strcoll(a
, b
);
847 unsigned long long ua
= strtoull(a
+ i
, NULL
, 10);
848 unsigned long long ub
= strtoull(b
+ i
, NULL
, 10);
850 /* The number may be identical in two cases:
851 * - leading zero (e.g. "012" and "12")
852 * - overflow on both sides (#ULLONG_MAX) */
854 return strcoll(a
, b
);
856 return (ua
> ub
) ? +1 : -1;
860 * Sanitize a file name.
862 * Remove forbidden, potentially forbidden and otherwise evil characters from
863 * file names. That includes slashes, and popular characters like colon
866 * \warning This function should only be used for automatically generated
867 * file names. Do not use this on full paths, only single file names without
868 * any directory separator!
870 void filename_sanitize( char *str
)
874 /* Special file names, not allowed */
875 if( !strcmp( str
, "." ) || !strcmp( str
, ".." ) )
882 /* On platforms not using UTF-8, VLC cannot access non-Unicode paths.
883 * Also, some file systems require Unicode file names.
884 * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
887 /* Avoid leading spaces to please Windows. */
888 while( (c
= *str
) != '\0' )
897 while( (c
= *str
) != '\0' )
899 /* Non-printable characters are not a good idea */
902 /* This is the list of characters not allowed by Microsoft.
903 * We also black-list them on Unix as they may be confusing, and are
904 * not supported by some file system types (notably CIFS). */
905 else if( strchr( "/:\\*\"?|<>", c
) != NULL
)
910 /* Avoid trailing spaces also to please Windows. */
913 if( *(--str
) != ' ' )