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>
45 #include <vlc_playlist.h>
47 #include <vlc_strings.h>
48 #include <vlc_charset.h>
53 static const struct xml_entity_s
58 /* Important: this list has to be in alphabetical order (psz_entity-wise) */
140 { "nbsp;", "\xc2\xa0" },
185 static int cmp_entity (const void *key, const void *elem)
187 const struct xml_entity_s *ent = elem;
188 const char *name = key;
190 return strncmp (name, ent->psz_entity, strlen (ent->psz_entity));
194 * Converts "<", ">" and "&" to "<", ">" and "&"
195 * \param string to convert
197 void resolve_xml_special_chars( char *psz_value )
199 char *p_pos = psz_value;
203 if( *psz_value == '&' )
205 if( psz_value[1] == '#' )
206 { /* &#xxx; Unicode code point */
208 unsigned long cp = strtoul( psz_value+2, &psz_end, 10 );
209 if( *psz_end == ';' )
211 psz_value = psz_end + 1;
213 (void)0; /* skip nuls */
220 /* Unicode code point outside ASCII.
221 * &#xxx; representation is longer than UTF-8 :) */
224 *p_pos++ = 0xC0 | (cp >> 6);
225 *p_pos = 0x80 | (cp & 0x3F);
230 *p_pos++ = 0xE0 | (cp >> 12);
231 *p_pos++ = 0x80 | ((cp >> 6) & 0x3F);
232 *p_pos = 0x80 | (cp & 0x3F);
235 if( cp <= 0x1FFFFF ) /* Outside the BMP */
236 { /* Unicode stops at 10FFFF, but who cares? */
237 *p_pos++ = 0xF0 | (cp >> 18);
238 *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
239 *p_pos++ = 0x80 | ((cp >> 6) & 0x3F);
240 *p_pos = 0x80 | (cp & 0x3F);
245 /* Invalid entity number */
251 { /* Well-known XML entity */
252 const struct xml_entity_s *ent;
254 ent = bsearch (psz_value + 1, xml_entities,
255 sizeof (xml_entities) / sizeof (*ent),
256 sizeof (*ent), cmp_entity);
259 size_t olen = strlen (ent->psz_char);
260 memcpy (p_pos, ent->psz_char, olen);
262 psz_value += strlen (ent->psz_entity) + 1;
284 * XML-encode an UTF-8 string
285 * \param str nul-terminated UTF-8 byte sequence to XML-encode
286 * \return XML encoded string or NULL on error
287 * (errno is set to ENOMEM or EILSEQ as appropriate)
289 char *convert_xml_special_chars (const char *str)
291 assert (str != NULL);
293 const size_t len = strlen (str);
294 char *const buf = malloc (6 * len + 1), *ptr = buf;
295 if (unlikely(buf == NULL))
301 while ((n = vlc_towc (str, &cp)) != 0)
303 if (unlikely(n == (size_t)-1))
310 if ((cp & ~0x0080) < 32 /* C0/C1 control codes */
311 && memchr ("\x09\x0A\x0D\x85", cp, 4) == NULL)
312 ptr += sprintf (ptr, "&#%"PRIu32";", cp);
316 case '\"': strcpy (ptr, """); ptr += 6; break;
317 case '&': strcpy (ptr, "&"); ptr += 5; break;
318 case '\'': strcpy (ptr, "'"); ptr += 5; break;
319 case '<': strcpy (ptr, "<"); ptr += 4; break;
320 case '>': strcpy (ptr, ">"); ptr += 4; break;
321 default: memcpy (ptr, str, n); ptr += n; break;
327 ptr = realloc (buf, ptr - buf);
328 return likely(ptr != NULL) ? ptr : buf; /* cannot fail */
331 /* Base64 encoding */
332 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
334 static const char b64[] =
335 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
337 char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
345 /* pops (up to) 3 bytes of input, push 4 bytes */
350 *dst++ = b64[v >> 26];
356 *dst++ = b64[v >> 26];
361 v |= *src++ << 20; // 3/3
362 *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
366 *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
378 char *vlc_b64_encode( const char *src )
381 return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
383 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
386 /* Base64 decoding */
387 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
389 static const int b64[256] = {
390 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
391 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
392 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
393 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
394 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
395 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
396 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
397 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
398 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
399 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
400 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
401 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
402 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
403 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
404 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
405 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
407 uint8_t *p_start = p_dst;
408 uint8_t *p = (uint8_t *)p_src;
413 for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ )
415 const int c = b64[(unsigned int)*p];
425 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
429 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
433 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
439 return p_dst - p_start;
441 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
443 const int i_src = strlen( psz_src );
446 *pp_dst = p_dst = malloc( i_src );
449 return vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
451 char *vlc_b64_decode( const char *psz_src )
453 const int i_src = strlen( psz_src );
454 char *p_dst = malloc( i_src + 1 );
459 i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
466 * Formats current time into a heap-allocated string.
467 * @param tformat time format (as with C strftime())
468 * @return an allocated string (must be free()'d), or NULL on memory error.
470 char *str_format_time( const char *tformat )
475 if (strcmp (tformat, "") == 0)
476 return strdup (""); /* corner case w.r.t. strftime() return value */
478 /* Get the current time. */
481 /* Convert it to local time representation. */
482 localtime_r( &curtime, &loctime );
483 for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
485 char *str = malloc (buflen);
489 size_t len = strftime (str, buflen, tformat, &loctime);
492 char *ret = realloc (str, len + 1);
493 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 int 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 int len = strlen( string ); \
525 dst = xrealloc( dst, i_size = i_size + len );\
526 memcpy( dst+d, string, len ); \
529 char *str_format_meta( playlist_t *p_object, const char *string )
531 const char *s = string;
532 bool b_is_format = false;
533 bool b_empty_if_na = false;
535 int i_size = strlen( string ) + 1; /* +1 to store '\0' */
536 char *dst = strdup( string );
537 if( !dst ) return NULL;
540 input_thread_t *p_input = playlist_CurrentInput( p_object );
541 input_item_t *p_item = NULL;
544 p_item = input_GetItem(p_input);
556 INSERT_STRING( input_item_GetArtist( p_item ) );
562 INSERT_STRING( input_item_GetAlbum( p_item ) );
568 INSERT_STRING( input_item_GetCopyright( p_item ) );
574 INSERT_STRING( input_item_GetDescription( p_item ) );
580 INSERT_STRING( input_item_GetEncodedBy( p_item ) );
584 if( p_item && p_item->p_stats )
586 vlc_mutex_lock( &p_item->p_stats->lock );
587 snprintf( buf, 10, "%"PRIi64,
588 p_item->p_stats->i_displayed_pictures );
589 vlc_mutex_unlock( &p_item->p_stats->lock );
592 strcpy( buf, b_empty_if_na ? "" : "-" );
593 INSERT_STRING_NO_FREE( buf );
598 INSERT_STRING( input_item_GetGenre( p_item ) );
604 INSERT_STRING( input_item_GetLanguage( p_item ) );
610 INSERT_STRING( input_item_GetTrackNum( p_item ) );
616 INSERT_STRING( input_item_GetNowPlaying( p_item ) );
622 INSERT_STRING( input_item_GetRating( p_item ) );
627 char *psz_lang = NULL;
629 psz_lang = var_GetNonEmptyString( p_input, "sub-language" );
630 if( psz_lang == NULL )
631 psz_lang = strdup( b_empty_if_na ? "" : "-" );
632 INSERT_STRING( psz_lang );
638 INSERT_STRING( input_item_GetTitle( p_item ) );
644 INSERT_STRING( input_item_GetURL( p_item ) );
650 INSERT_STRING( input_item_GetDate( p_item ) );
656 snprintf( buf, 10, "%"PRId64,
657 var_GetInteger( p_input, "bit-rate" )/1000 );
660 strcpy( buf, b_empty_if_na ? "" : "-" );
661 INSERT_STRING_NO_FREE( buf );
666 snprintf( buf, 10, "%"PRId64,
667 var_GetInteger( p_input, "chapter" ) );
670 strcpy( buf, b_empty_if_na ? "" : "-" );
671 INSERT_STRING_NO_FREE( buf );
676 mtime_t i_duration = input_item_GetDuration( p_item );
677 format_duration (buf, sizeof (buf), i_duration);
680 strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
681 INSERT_STRING_NO_FREE( buf );
686 INSERT_STRING( input_item_GetURI( p_item ) );
692 snprintf( buf, 10, "%"PRId64,
693 var_GetInteger( p_input, "title" ) );
696 strcpy( buf, b_empty_if_na ? "" : "-" );
697 INSERT_STRING_NO_FREE( buf );
700 if( p_item && p_input )
702 mtime_t i_duration = input_item_GetDuration( p_item );
703 int64_t i_time = var_GetTime( p_input, "time" );
704 format_duration( buf, sizeof(buf),
705 i_duration - i_time );
708 strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
709 INSERT_STRING_NO_FREE( buf );
714 INSERT_STRING( input_item_GetName( p_item ) );
721 lang = var_GetNonEmptyString( p_input,
724 lang = strdup( b_empty_if_na ? "" : "-" );
725 INSERT_STRING( lang );
731 snprintf( buf, 10, "%2.1lf",
732 var_GetFloat( p_input, "position" ) * 100. );
736 snprintf( buf, 10, b_empty_if_na ? "" : "--.-%%" );
738 INSERT_STRING_NO_FREE( buf );
743 float f = var_GetFloat( p_input, "rate" );
744 snprintf( buf, 10, "%.3f", f );
747 strcpy( buf, b_empty_if_na ? "" : "-" );
748 INSERT_STRING_NO_FREE( buf );
753 int r = var_GetInteger( p_input, "sample-rate" );
754 snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
757 strcpy( buf, b_empty_if_na ? "" : "-" );
758 INSERT_STRING_NO_FREE( buf );
763 int64_t i_time = var_GetTime( p_input, "time" );
764 format_duration( buf, sizeof(buf), i_time );
767 strcpy( buf, b_empty_if_na ? "" : "--:--:--" );
768 INSERT_STRING_NO_FREE( buf );
773 INSERT_STRING( input_item_GetPublisher( p_item ) );
778 float vol = playlist_VolumeGet( p_object );
781 snprintf( buf, 10, "%ld",
782 lroundf(vol * AOUT_VOLUME_DEFAULT ) );
783 INSERT_STRING_NO_FREE( buf );
786 INSERT_STRING_NO_FREE( "---" );
796 char *psz_now_playing = input_item_GetNowPlaying( p_item );
797 if( EMPTY_STR( psz_now_playing ) )
799 char *psz_temp = input_item_GetTitleFbName( p_item );
800 char *psz_artist = input_item_GetArtist( p_item );
801 if( !EMPTY_STR( psz_artist ) )
803 INSERT_STRING( psz_artist );
804 if ( !EMPTY_STR( psz_temp ) )
805 INSERT_STRING_NO_FREE( " - " );
807 INSERT_STRING( psz_temp );
810 INSERT_STRING( psz_now_playing );
815 b_empty_if_na = true;
829 b_empty_if_na = false;
841 vlc_object_release( p_input );
846 #undef INSERT_STRING_NO_FREE
849 * Remove forbidden, potentially forbidden and otherwise evil characters from
850 * filenames. This includes slashes, and popular characters like colon
851 * (on Unix anyway), so this should only be used for automatically generated
853 * \warning Do not use this on full paths,
854 * only single file names without any directory separator!
856 void filename_sanitize( char *str )
860 /* Special file names, not allowed */
861 if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
868 /* On platforms not using UTF-7, VLC cannot access non-Unicode paths.
869 * Also, some file systems require Unicode file names.
870 * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
873 /* Avoid leading spaces to please Windows. */
874 while( (c = *str) != '\0' )
883 while( (c = *str) != '\0' )
885 /* Non-printable characters are not a good idea */
888 /* This is the list of characters not allowed by Microsoft.
889 * We also black-list them on Unix as they may be confusing, and are
890 * not supported by some file system types (notably CIFS). */
891 else if( strchr( "/:\\*\"?|<>", c ) != NULL )
896 /* Avoid trailing spaces also to please Windows. */
899 if( *(--str) != ' ' )
906 * Remove forbidden characters from full paths (leaves slashes)
908 void path_sanitize( char *str )
910 #if defined( WIN32 ) || defined( __OS2__ )
911 /* check drive prefix if path is absolute */
912 if( (((unsigned char)(str[0] - 'A') < 26)
913 || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) )
918 #if defined( __APPLE__ )
921 #elif defined( WIN32 ) || defined( __OS2__ )
922 if( strchr( "*\"?:|<>", *str ) )
932 Decodes a duration as defined by ISO 8601
933 http://en.wikipedia.org/wiki/ISO_8601#Durations
934 @param str A null-terminated string to convert
935 @return: The duration in seconds. -1 if an error occurred.
937 Exemple input string: "PT0H9M56.46S"
939 time_t str_duration( const char *psz_duration )
941 bool timeDesignatorReached = false;
945 if ( psz_duration == NULL )
947 if ( ( *(psz_duration++) ) != 'P' )
951 double number = strtod( psz_duration, &end_ptr );
953 if ( psz_duration != end_ptr )
954 psz_duration = end_ptr;
955 switch( *psz_duration )
959 //M can mean month or minutes, if the 'T' flag has been reached.
960 //We don't handle months though.
961 if ( timeDesignatorReached == true )
967 break ; //Don't handle this duration.
972 timeDesignatorReached = true;
983 res += (time_t)(mul * number);
986 } while ( *psz_duration );