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 { /* &#DDD; or &#xHHHH; Unicode code point */
209 if( psz_value[2] == 'x' ) /* The x must be lower-case. */
210 cp = strtoul( psz_value + 3, &psz_end, 16 );
212 cp = strtoul( psz_value + 2, &psz_end, 10 );
214 if( *psz_end == ';' )
216 psz_value = psz_end + 1;
218 (void)0; /* skip nuls */
225 /* Unicode code point outside ASCII.
226 * &#xxx; representation is longer than UTF-8 :) */
229 *p_pos++ = 0xC0 | (cp >> 6);
230 *p_pos = 0x80 | (cp & 0x3F);
235 *p_pos++ = 0xE0 | (cp >> 12);
236 *p_pos++ = 0x80 | ((cp >> 6) & 0x3F);
237 *p_pos = 0x80 | (cp & 0x3F);
240 if( cp <= 0x1FFFFF ) /* Outside the BMP */
241 { /* Unicode stops at 10FFFF, but who cares? */
242 *p_pos++ = 0xF0 | (cp >> 18);
243 *p_pos++ = 0x80 | ((cp >> 12) & 0x3F);
244 *p_pos++ = 0x80 | ((cp >> 6) & 0x3F);
245 *p_pos = 0x80 | (cp & 0x3F);
250 /* Invalid entity number */
256 { /* Well-known XML entity */
257 const struct xml_entity_s *ent;
259 ent = bsearch (psz_value + 1, xml_entities,
260 sizeof (xml_entities) / sizeof (*ent),
261 sizeof (*ent), cmp_entity);
264 size_t olen = strlen (ent->psz_char);
265 memcpy (p_pos, ent->psz_char, olen);
267 psz_value += strlen (ent->psz_entity) + 1;
289 * XML-encode an UTF-8 string
290 * \param str nul-terminated UTF-8 byte sequence to XML-encode
291 * \return XML encoded string or NULL on error
292 * (errno is set to ENOMEM or EILSEQ as appropriate)
294 char *convert_xml_special_chars (const char *str)
296 assert (str != NULL);
298 const size_t len = strlen (str);
299 char *const buf = malloc (6 * len + 1), *ptr = buf;
300 if (unlikely(buf == NULL))
306 while ((n = vlc_towc (str, &cp)) != 0)
308 if (unlikely(n == (size_t)-1))
315 if ((cp & ~0x0080) < 32 /* C0/C1 control codes */
316 && memchr ("\x09\x0A\x0D\x85", cp, 4) == NULL)
317 ptr += sprintf (ptr, "&#%"PRIu32";", cp);
321 case '\"': strcpy (ptr, """); ptr += 6; break;
322 case '&': strcpy (ptr, "&"); ptr += 5; break;
323 case '\'': strcpy (ptr, "'"); ptr += 5; break;
324 case '<': strcpy (ptr, "<"); ptr += 4; break;
325 case '>': strcpy (ptr, ">"); ptr += 4; break;
326 default: memcpy (ptr, str, n); ptr += n; break;
332 ptr = realloc (buf, ptr - buf);
333 return likely(ptr != NULL) ? ptr : buf; /* cannot fail */
336 /* Base64 encoding */
337 char *vlc_b64_encode_binary( const uint8_t *src, size_t i_src )
339 static const char b64[] =
340 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
342 char *ret = malloc( ( i_src + 4 ) * 4 / 3 );
350 /* pops (up to) 3 bytes of input, push 4 bytes */
355 *dst++ = b64[v >> 26];
361 *dst++ = b64[v >> 26];
366 v |= *src++ << 20; // 3/3
367 *dst++ = ( i_src >= 2 ) ? b64[v >> 26] : '='; // 3/4
371 *dst++ = ( i_src >= 3 ) ? b64[v >> 26] : '='; // 4/4
383 char *vlc_b64_encode( const char *src )
386 return vlc_b64_encode_binary( (const uint8_t*)src, strlen(src) );
388 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
391 /* Base64 decoding */
392 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char *p_src )
394 static const int b64[256] = {
395 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
396 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
397 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
398 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
399 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
400 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
401 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
402 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
403 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
404 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
405 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
406 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
407 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
408 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
409 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
410 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
412 uint8_t *p_start = p_dst;
413 uint8_t *p = (uint8_t *)p_src;
418 for( i_level = 0, i_last = 0; (size_t)( p_dst - p_start ) < i_dst && *p != '\0'; p++ )
420 const int c = b64[(unsigned int)*p];
430 *p_dst++ = ( i_last << 2 ) | ( ( c >> 4)&0x03 );
434 *p_dst++ = ( ( i_last << 4 )&0xf0 ) | ( ( c >> 2 )&0x0f );
438 *p_dst++ = ( ( i_last &0x03 ) << 6 ) | c;
444 return p_dst - p_start;
446 size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src )
448 const int i_src = strlen( psz_src );
451 *pp_dst = p_dst = malloc( i_src );
454 return vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src );
456 char *vlc_b64_decode( const char *psz_src )
458 const int i_src = strlen( psz_src );
459 char *p_dst = malloc( i_src + 1 );
464 i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src );
471 * Formats current time into a heap-allocated string.
472 * @param tformat time format (as with C strftime())
473 * @return an allocated string (must be free()'d), or NULL on memory error.
475 char *str_format_time( const char *tformat )
480 if (strcmp (tformat, "") == 0)
481 return strdup (""); /* corner case w.r.t. strftime() return value */
483 /* Get the current time. */
486 /* Convert it to local time representation. */
487 localtime_r( &curtime, &loctime );
488 for (size_t buflen = strlen (tformat) + 32;; buflen += 32)
490 char *str = malloc (buflen);
494 size_t len = strftime (str, buflen, tformat, &loctime);
497 char *ret = realloc (str, len + 1);
498 return ret ? ret : str; /* <- this cannot fail */
502 vlc_assert_unreachable ();
505 static void write_duration(FILE *stream, int64_t duration)
510 duration /= CLOCK_FREQ;
511 d = lldiv(duration, 60);
513 d = lldiv(d.quot, 60);
514 fprintf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec);
517 static int write_meta(FILE *stream, input_item_t *item, vlc_meta_type_t type)
522 char *value = input_item_GetMeta(item, type);
526 int ret = fputs(value, stream);
531 char *str_format_meta(input_thread_t *input, const char *s)
535 #ifdef HAVE_OPEN_MEMSTREAM
536 FILE *stream = open_memstream(&str, &len);
537 #elif defined( _WIN32 )
538 FILE *stream = vlc_win32_tmpfile();
540 FILE *stream = tmpfile();
545 input_item_t *item = (input != NULL) ? input_GetItem(input) : NULL;
548 bool b_is_format = false;
549 bool b_empty_if_na = false;
551 while ((c = *s) != '\0')
560 b_empty_if_na = false;
573 write_meta(stream, item, vlc_meta_Artist);
576 write_meta(stream, item, vlc_meta_Album);
579 write_meta(stream, item, vlc_meta_Copyright);
582 write_meta(stream, item, vlc_meta_Description);
585 write_meta(stream, item, vlc_meta_EncodedBy);
588 if (item != NULL && item->p_stats != NULL)
590 vlc_mutex_lock(&item->p_stats->lock);
591 fprintf(stream, "%"PRIi64,
592 item->p_stats->i_displayed_pictures);
593 vlc_mutex_unlock(&item->p_stats->lock);
595 else if (!b_empty_if_na)
599 write_meta(stream, item, vlc_meta_Genre);
602 write_meta(stream, item, vlc_meta_Language);
605 write_meta(stream, item, vlc_meta_TrackNumber);
611 char *value = input_item_GetNowPlayingFb(item);
615 fputs(value, stream);
620 write_meta(stream, item, vlc_meta_Rating);
627 lang = var_GetNonEmptyString(input, "sub-language");
633 else if (!b_empty_if_na)
638 write_meta(stream, item, vlc_meta_Title);
641 write_meta(stream, item, vlc_meta_URL);
644 write_meta(stream, item, vlc_meta_Date);
648 fprintf(stream, "%"PRId64,
649 var_GetInteger(input, "bit-rate") / 1000);
650 else if (!b_empty_if_na)
655 fprintf(stream, "%"PRId64,
656 var_GetInteger(input, "chapter"));
657 else if (!b_empty_if_na)
662 write_duration(stream, input_item_GetDuration(item));
663 else if (!b_empty_if_na)
664 fputs("--:--:--", stream);
669 char *uri = input_item_GetURI(item);
679 fprintf(stream, "%"PRId64, var_GetInteger(input, "title"));
680 else if (!b_empty_if_na)
686 assert(input != NULL);
687 write_duration(stream, input_item_GetDuration(item)
688 - var_GetTime(input, "time"));
690 else if (!b_empty_if_na)
691 fputs("--:--:--", stream);
696 char *name = input_item_GetName(item);
709 lang = var_GetNonEmptyString(input, "audio-language");
715 else if (!b_empty_if_na)
721 fprintf(stream, "%2.1f",
722 var_GetFloat(input, "position") * 100.f);
723 else if (!b_empty_if_na)
724 fputs("--.-%", stream);
728 fprintf(stream, "%.3f", var_GetFloat(input, "rate"));
729 else if (!b_empty_if_na)
735 int rate = var_GetInteger(input, "sample-rate");
736 div_t dr = div((rate + 50) / 100, 10);
738 fprintf(stream, "%d.%01d", dr.quot, dr.rem);
740 else if (!b_empty_if_na)
745 write_duration(stream, var_GetTime(input, "time"));
746 else if (!b_empty_if_na)
747 fputs("--:--:--", stream);
750 write_meta(stream, item, vlc_meta_Publisher);
758 audio_output_t *aout = input_GetAout(input);
761 vol = aout_VolumeGet(aout);
762 vlc_object_release(aout);
766 fprintf(stream, "%ld", lroundf(vol * 256.f));
767 else if (!b_empty_if_na)
768 fputs("---", stream);
778 char *value = input_item_GetNowPlayingFb(item);
781 fputs(value, stream);
786 char *title = input_item_GetTitleFbName(item);
788 if (write_meta(stream, item, vlc_meta_Artist) >= 0
790 fputs(" - ", stream);
794 fputs(title, stream);
801 b_empty_if_na = true;
810 #ifdef HAVE_OPEN_MEMSTREAM
811 return (fclose(stream) == 0) ? str : NULL;
814 if (len != (size_t)-1)
817 str = xmalloc(len + 1);
818 fread(str, len, 1, stream);
829 * Remove forbidden, potentially forbidden and otherwise evil characters from
830 * filenames. This includes slashes, and popular characters like colon
831 * (on Unix anyway), so this should only be used for automatically generated
833 * \warning Do not use this on full paths,
834 * only single file names without any directory separator!
836 void filename_sanitize( char *str )
840 /* Special file names, not allowed */
841 if( !strcmp( str, "." ) || !strcmp( str, ".." ) )
848 /* On platforms not using UTF-7, VLC cannot access non-Unicode paths.
849 * Also, some file systems require Unicode file names.
850 * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
853 /* Avoid leading spaces to please Windows. */
854 while( (c = *str) != '\0' )
863 while( (c = *str) != '\0' )
865 /* Non-printable characters are not a good idea */
868 /* This is the list of characters not allowed by Microsoft.
869 * We also black-list them on Unix as they may be confusing, and are
870 * not supported by some file system types (notably CIFS). */
871 else if( strchr( "/:\\*\"?|<>", c ) != NULL )
876 /* Avoid trailing spaces also to please Windows. */
879 if( *(--str) != ' ' )
886 * Remove forbidden characters from full paths (leaves slashes)
888 void path_sanitize( char *str )
890 #if defined( _WIN32 ) || defined( __OS2__ )
891 /* check drive prefix if path is absolute */
892 if( (((unsigned char)(str[0] - 'A') < 26)
893 || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) )
898 #if defined( __APPLE__ )
901 #elif defined( _WIN32 ) || defined( __OS2__ )
902 if( strchr( "*\"?:|<>", *str ) )
912 Decodes a duration as defined by ISO 8601
913 http://en.wikipedia.org/wiki/ISO_8601#Durations
914 @param str A null-terminated string to convert
915 @return: The duration in seconds. -1 if an error occurred.
917 Exemple input string: "PT0H9M56.46S"
919 time_t str_duration( const char *psz_duration )
921 bool timeDesignatorReached = false;
925 if ( psz_duration == NULL )
927 if ( ( *(psz_duration++) ) != 'P' )
931 double number = us_strtod( psz_duration, &end_ptr );
933 if ( psz_duration != end_ptr )
934 psz_duration = end_ptr;
935 switch( *psz_duration )
939 //M can mean month or minutes, if the 'T' flag has been reached.
940 //We don't handle months though.
941 if ( timeDesignatorReached == true )
947 break ; //Don't handle this duration.
952 timeDesignatorReached = true;
963 res += (time_t)(mul * number);
966 } while ( *psz_duration );