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 str_format_time */
42 /* Needed by str_format_meta */
43 #include <vlc_input.h>
47 #include <vlc_strings.h>
48 #include <vlc_charset.h>
52 static const struct xml_entity_s
57 /* Important: this list has to be in alphabetical order (psz_entity-wise) */
139 { "nbsp;", "\xc2\xa0" },
184 static int cmp_entity (const void *key, const void *elem)
186 const struct xml_entity_s *ent = elem;
187 const char *name = key;
189 return strncmp (name, ent->psz_entity, strlen (ent->psz_entity));
193 * Converts "<", ">" and "&" to "<", ">" and "&"
194 * \param string to convert
196 void resolve_xml_special_chars( char *psz_value )
198 char *p_pos = psz_value;
202 if( *psz_value == '&' )
204 if( psz_value[1] == '#' )
205 { /* &#xxx; Unicode code point */
207 unsigned long cp = strtoul( psz_value+2, &psz_end, 10 );
208 if( *psz_end == ';' )
210 psz_value = psz_end + 1;
212 (void)0; /* skip nuls */
219 /* Unicode code point outside ASCII.
220 * &#xxx; representation is longer than UTF-8 :) */
223 *p_pos++ = 0xC0 | (cp >> 6);
224 *p_pos = 0x80 | (cp & 0x3F);
229 *p_pos++ = 0xE0 | (cp >> 12);
230 *p_pos++ = 0x80 | ((cp >> 6) & 0x3F);
231 *p_pos = 0x80 | (cp & 0x3F);
234 if( cp <= 0x1FFFFF ) /* Outside the BMP */
235 { /* Unicode stops at 10FFFF, but who cares? */
236 *p_pos++ = 0xF0 | (cp >> 18);
237 *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
238 *p_pos++ = 0x80 | ((cp >> 6) & 0x3F);
239 *p_pos = 0x80 | (cp & 0x3F);
244 /* Invalid entity number */
250 { /* Well-known XML entity */
251 const struct xml_entity_s *ent;
253 ent = bsearch (psz_value + 1, xml_entities,
254 sizeof (xml_entities) / sizeof (*ent),
255 sizeof (*ent), cmp_entity);
258 size_t olen = strlen (ent->psz_char);
259 memcpy (p_pos, ent->psz_char, olen);
261 psz_value += strlen (ent->psz_entity) + 1;
283 * XML-encode an UTF-8 string
284 * \param str nul-terminated UTF-8 byte sequence to XML-encode
285 * \return XML encoded string or NULL on error
286 * (errno is set to ENOMEM or EILSEQ as appropriate)
288 char *convert_xml_special_chars (const char *str)
290 assert (str != NULL);
292 const size_t len = strlen (str);
293 char *const buf = malloc (6 * len + 1), *ptr = buf;
294 if (unlikely(buf == NULL))
300 while ((n = vlc_towc (str, &cp)) != 0)
302 if (unlikely(n == (size_t)-1))
309 if ((cp & ~0x0080) < 32 /* C0/C1 control codes */
310 && memchr ("\x09\x0A\x0D\x85", cp, 4) == NULL)
311 ptr += sprintf (ptr, "&#%"PRIu32";", cp);
315 case '\"': strcpy (ptr, """); ptr += 6; break;
316 case '&': strcpy (ptr, "&"); ptr += 5; break;
317 case '\'': strcpy (ptr, "'"); ptr += 5; break;
318 case '<': strcpy (ptr, "<"); ptr += 4; break;
319 case '>': strcpy (ptr, ">"); ptr += 4; break;
320 default: memcpy (ptr, str, n); ptr += n; break;
326 ptr = realloc (buf, ptr - buf);
327 return likely(ptr != NULL) ? ptr : buf; /* cannot fail */
330 /* Base64 encoding */
331 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
333 static const char b64[] =
334 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
336 char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
344 /* pops (up to) 3 bytes of input, push 4 bytes */
349 *dst++ = b64[v >> 26];
355 *dst++ = b64[v >> 26];
360 v |= *src++ << 20; // 3/3
361 *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
365 *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
377 char *vlc_b64_encode( const char *src )
380 return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
382 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
385 /* Base64 decoding */
386 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
388 static const int b64[256] = {
389 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
390 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
391 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
392 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
393 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
394 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
395 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
396 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
397 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
398 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
399 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
400 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
401 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
402 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
403 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
404 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
406 uint8_t *p_start = p_dst;
407 uint8_t *p = (uint8_t *)p_src;
412 for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ )
414 const int c = b64[(unsigned int)*p];
424 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
428 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
432 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
438 return p_dst - p_start;
440 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
442 const int i_src = strlen( psz_src );
445 *pp_dst = p_dst = malloc( i_src );
448 return vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
450 char *vlc_b64_decode( const char *psz_src )
452 const int i_src = strlen( psz_src );
453 char *p_dst = malloc( i_src + 1 );
458 i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
465 * Formats current time into a heap-allocated string.
466 * @param tformat time format (as with C strftime())
467 * @return an allocated string (must be free()'d), or NULL on memory error.
469 char *str_format_time( const char *tformat )
474 if (strcmp (tformat, "") == 0)
475 return strdup (""); /* corner case w.r.t. strftime() return value */
477 /* Get the current time. */
480 /* Convert it to local time representation. */
481 localtime_r( &curtime, &loctime );
482 for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
484 char *str = malloc (buflen);
488 size_t len = strftime (str, buflen, tformat, &loctime);
491 char *ret = realloc (str, len + 1);
492 return ret ? ret : str; /* <- this cannot fail */
499 static void format_duration (char *buf, size_t len, int64_t duration)
504 duration /= CLOCK_FREQ;
505 d = lldiv (duration, 60);
507 d = lldiv (d.quot, 60);
508 snprintf (buf, len, "%02lld:%02d:%02d", d.quot, (int)d.rem, sec);
511 #define INSERT_STRING( string ) \
512 if( string != NULL ) \
514 size_t len = strlen( string ); \
515 dst = xrealloc( dst, i_size = i_size + len );\
516 memcpy( (dst+d), string, len ); \
521 /* same than INSERT_STRING, except that string won't be freed */
522 #define INSERT_STRING_NO_FREE( string ) \
524 size_t len = strlen( string ); \
525 dst = xrealloc( dst, i_size = i_size + len );\
526 memcpy( dst+d, string, len ); \
529 char *str_format_meta( input_thread_t *p_input, const char *s )
531 char *dst = strdup( s );
532 if( unlikely(dst == NULL) )
535 input_item_t *p_item = p_input ? input_GetItem(p_input) : NULL;
536 size_t i_size = strlen( s ) + 1; /* +1 to store '\0' */
539 bool b_is_format = false;
540 bool b_empty_if_na = false;
551 INSERT_STRING( input_item_GetArtist( p_item ) );
555 INSERT_STRING( input_item_GetAlbum( p_item ) );
559 INSERT_STRING( input_item_GetCopyright( p_item ) );
563 INSERT_STRING( input_item_GetDescription( p_item ) );
567 INSERT_STRING( input_item_GetEncodedBy( p_item ) );
570 if( p_item && p_item->p_stats )
572 vlc_mutex_lock( &p_item->p_stats->lock );
573 snprintf( buf, 10, "%"PRIi64,
574 p_item->p_stats->i_displayed_pictures );
575 vlc_mutex_unlock( &p_item->p_stats->lock );
578 strcpy( buf, b_empty_if_na ? "" : "-" );
579 INSERT_STRING_NO_FREE( buf );
583 INSERT_STRING( input_item_GetGenre( p_item ) );
587 INSERT_STRING( input_item_GetLanguage( p_item ) );
591 INSERT_STRING( input_item_GetTrackNum( p_item ) );
595 INSERT_STRING( input_item_GetNowPlaying( p_item ) );
599 INSERT_STRING( input_item_GetRating( p_item ) );
603 char *psz_lang = NULL;
605 psz_lang = var_GetNonEmptyString( p_input, "sub-language" );
606 if( psz_lang == NULL )
607 psz_lang = strdup( b_empty_if_na ? "" : "-" );
608 INSERT_STRING( psz_lang );
613 INSERT_STRING( input_item_GetTitle( p_item ) );
617 INSERT_STRING( input_item_GetURL( p_item ) );
621 INSERT_STRING( input_item_GetDate( p_item ) );
625 snprintf( buf, 10, "%"PRId64,
626 var_GetInteger( p_input, "bit-rate" )/1000 );
628 strcpy( buf, b_empty_if_na ? "" : "-" );
629 INSERT_STRING_NO_FREE( buf );
633 snprintf( buf, 10, "%"PRId64,
634 var_GetInteger( p_input, "chapter" ) );
636 strcpy( buf, b_empty_if_na ? "" : "-" );
637 INSERT_STRING_NO_FREE( buf );
642 mtime_t i_duration = input_item_GetDuration( p_item );
643 format_duration (buf, sizeof (buf), i_duration);
646 strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
647 INSERT_STRING_NO_FREE( buf );
651 INSERT_STRING( input_item_GetURI( p_item ) );
655 snprintf( buf, 10, "%"PRId64,
656 var_GetInteger( p_input, "title" ) );
658 strcpy( buf, b_empty_if_na ? "" : "-" );
659 INSERT_STRING_NO_FREE( buf );
662 if( p_item && p_input )
664 mtime_t i_duration = input_item_GetDuration( p_item );
665 int64_t i_time = var_GetTime( p_input, "time" );
666 format_duration( buf, sizeof(buf),
667 i_duration - i_time );
670 strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
671 INSERT_STRING_NO_FREE( buf );
675 INSERT_STRING( input_item_GetName( p_item ) );
681 lang = var_GetNonEmptyString( p_input,
684 lang = strdup( b_empty_if_na ? "" : "-" );
685 INSERT_STRING( lang );
690 snprintf( buf, 10, "%2.1lf",
691 var_GetFloat( p_input, "position" ) * 100. );
693 snprintf( buf, 10, b_empty_if_na ? "" : "--.-%%" );
694 INSERT_STRING_NO_FREE( buf );
699 float f = var_GetFloat( p_input, "rate" );
700 snprintf( buf, 10, "%.3f", f );
703 strcpy( buf, b_empty_if_na ? "" : "-" );
704 INSERT_STRING_NO_FREE( buf );
709 int r = var_GetInteger( p_input, "sample-rate" );
710 snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
713 strcpy( buf, b_empty_if_na ? "" : "-" );
714 INSERT_STRING_NO_FREE( buf );
719 int64_t i_time = var_GetTime( p_input, "time" );
720 format_duration( buf, sizeof(buf), i_time );
723 strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
724 INSERT_STRING_NO_FREE( buf );
728 INSERT_STRING( input_item_GetPublisher( p_item ) );
736 audio_output_t *aout = input_GetAout( p_input );
739 vol = aout_VolumeGet( aout );
740 vlc_object_release( aout );
745 snprintf( buf, 10, "%ld", lroundf(vol * 256.f) );
746 INSERT_STRING_NO_FREE( buf );
749 INSERT_STRING_NO_FREE( "---" );
759 char *psz_now_playing = input_item_GetNowPlaying( p_item );
760 if( EMPTY_STR( psz_now_playing ) )
762 char *psz_temp = input_item_GetTitleFbName( p_item );
763 char *psz_artist = input_item_GetArtist( p_item );
764 if( !EMPTY_STR( psz_artist ) )
766 INSERT_STRING( psz_artist );
767 if ( !EMPTY_STR( psz_temp ) )
768 INSERT_STRING_NO_FREE( " - " );
770 INSERT_STRING( psz_temp );
773 INSERT_STRING( psz_now_playing );
778 b_empty_if_na = true;
792 b_empty_if_na = false;
806 #undef INSERT_STRING_NO_FREE
809 * Remove forbidden, potentially forbidden and otherwise evil characters from
810 * filenames. This includes slashes, and popular characters like colon
811 * (on Unix anyway), so this should only be used for automatically generated
813 * \warning Do not use this on full paths,
814 * only single file names without any directory separator!
816 void filename_sanitize( char *str )
820 /* Special file names, not allowed */
821 if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
828 /* On platforms not using UTF-7, VLC cannot access non-Unicode paths.
829 * Also, some file systems require Unicode file names.
830 * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
833 /* Avoid leading spaces to please Windows. */
834 while( (c = *str) != '\0' )
843 while( (c = *str) != '\0' )
845 /* Non-printable characters are not a good idea */
848 /* This is the list of characters not allowed by Microsoft.
849 * We also black-list them on Unix as they may be confusing, and are
850 * not supported by some file system types (notably CIFS). */
851 else if( strchr( "/:\\*\"?|<>", c ) != NULL )
856 /* Avoid trailing spaces also to please Windows. */
859 if( *(--str) != ' ' )
866 * Remove forbidden characters from full paths (leaves slashes)
868 void path_sanitize( char *str )
870 #if defined( _WIN32 ) || defined( __OS2__ )
871 /* check drive prefix if path is absolute */
872 if( (((unsigned char)(str[0] - 'A') < 26)
873 || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) )
878 #if defined( __APPLE__ )
881 #elif defined( _WIN32 ) || defined( __OS2__ )
882 if( strchr( "*\"?:|<>", *str ) )
892 Decodes a duration as defined by ISO 8601
893 http://en.wikipedia.org/wiki/ISO_8601#Durations
894 @param str A null-terminated string to convert
895 @return: The duration in seconds. -1 if an error occurred.
897 Exemple input string: "PT0H9M56.46S"
899 time_t str_duration( const char *psz_duration )
901 bool timeDesignatorReached = false;
905 if ( psz_duration == NULL )
907 if ( ( *(psz_duration++) ) != 'P' )
911 double number = strtod( psz_duration, &end_ptr );
913 if ( psz_duration != end_ptr )
914 psz_duration = end_ptr;
915 switch( *psz_duration )
919 //M can mean month or minutes, if the 'T' flag has been reached.
920 //We don't handle months though.
921 if ( timeDesignatorReached == true )
927 break ; //Don't handle this duration.
932 timeDesignatorReached = true;
943 res += (time_t)(mul * number);
946 } while ( *psz_duration );