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 */
42 /* Needed by vlc_strfinput */
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
));
192 void vlc_xml_decode( char *psz_value
)
194 char *p_pos
= psz_value
;
198 if( *psz_value
== '&' )
200 if( psz_value
[1] == '#' )
201 { /* &#DDD; or &#xHHHH; Unicode code point */
205 if( psz_value
[2] == 'x' ) /* The x must be lower-case. */
206 cp
= strtoul( psz_value
+ 3, &psz_end
, 16 );
208 cp
= strtoul( psz_value
+ 2, &psz_end
, 10 );
210 if( *psz_end
== ';' )
212 psz_value
= psz_end
+ 1;
214 (void)0; /* skip nulls */
221 /* Unicode code point outside ASCII.
222 * &#xxx; representation is longer than UTF-8 :) */
225 *p_pos
++ = 0xC0 | (cp
>> 6);
226 *p_pos
= 0x80 | (cp
& 0x3F);
231 *p_pos
++ = 0xE0 | (cp
>> 12);
232 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
233 *p_pos
= 0x80 | (cp
& 0x3F);
236 if( cp
<= 0x1FFFFF ) /* Outside the BMP */
237 { /* Unicode stops at 10FFFF, but who cares? */
238 *p_pos
++ = 0xF0 | (cp
>> 18);
239 *p_pos
++ = 0x80 | ((cp
>> 12) & 0x3F);
240 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
241 *p_pos
= 0x80 | (cp
& 0x3F);
246 /* Invalid entity number */
252 { /* Well-known XML entity */
253 const struct xml_entity_s
*ent
;
255 ent
= bsearch (psz_value
+ 1, xml_entities
,
256 sizeof (xml_entities
) / sizeof (*ent
),
257 sizeof (*ent
), cmp_entity
);
260 size_t olen
= strlen (ent
->psz_char
);
261 memcpy (p_pos
, ent
->psz_char
, olen
);
263 psz_value
+= strlen (ent
->psz_entity
) + 1;
284 char *vlc_xml_encode (const char *str
)
286 assert (str
!= NULL
);
288 const size_t len
= strlen (str
);
289 char *const buf
= malloc (6 * len
+ 1), *ptr
= buf
;
290 if (unlikely(buf
== NULL
))
296 while ((n
= vlc_towc (str
, &cp
)) != 0)
298 if (unlikely(n
== (size_t)-1))
307 case '\"': strcpy (ptr
, """); ptr
+= 6; break;
308 case '&': strcpy (ptr
, "&"); ptr
+= 5; break;
309 case '\'': strcpy (ptr
, "'"); ptr
+= 5; break;
310 case '<': strcpy (ptr
, "<"); ptr
+= 4; break;
311 case '>': strcpy (ptr
, ">"); ptr
+= 4; break;
313 if (cp
< 32) /* C0 code not allowed (except 9, 10 and 13) */
315 if (cp
>= 128 && cp
< 160) /* C1 code encoded (except 133) */
317 ptr
+= sprintf (ptr
, "&#%"PRIu32
";", cp
);
325 memcpy (ptr
, str
, n
);
333 ptr
= realloc (buf
, ptr
- buf
);
334 return likely(ptr
!= NULL
) ? ptr
: buf
; /* cannot fail */
337 /* Base64 encoding */
338 char *vlc_b64_encode_binary( const uint8_t *src
, size_t i_src
)
340 static const char b64
[] =
341 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
343 char *ret
= malloc( ( i_src
+ 4 ) * 4 / 3 );
351 /* pops (up to) 3 bytes of input, push 4 bytes */
356 *dst
++ = b64
[v
>> 26];
362 *dst
++ = b64
[v
>> 26];
367 v
|= *src
++ << 20; // 3/3
368 *dst
++ = ( i_src
>= 2 ) ? b64
[v
>> 26] : '='; // 3/4
372 *dst
++ = ( i_src
>= 3 ) ? b64
[v
>> 26] : '='; // 4/4
384 char *vlc_b64_encode( const char *src
)
387 return vlc_b64_encode_binary( (const uint8_t*)src
, strlen(src
) );
389 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
392 /* Base64 decoding */
393 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst
, size_t i_dst
, const char *p_src
)
395 static const int b64
[256] = {
396 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
397 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
398 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
399 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
400 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
401 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
402 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
403 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
404 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
405 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
406 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
407 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
408 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
409 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
410 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
411 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
413 uint8_t *p_start
= p_dst
;
414 uint8_t *p
= (uint8_t *)p_src
;
419 for( i_level
= 0, i_last
= 0; (size_t)( p_dst
- p_start
) < i_dst
&& *p
!= '\0'; p
++ )
421 const int c
= b64
[(unsigned int)*p
];
431 *p_dst
++ = ( i_last
<< 2 ) | ( ( c
>> 4)&0x03 );
435 *p_dst
++ = ( ( i_last
<< 4 )&0xf0 ) | ( ( c
>> 2 )&0x0f );
439 *p_dst
++ = ( ( i_last
&0x03 ) << 6 ) | c
;
445 return p_dst
- p_start
;
447 size_t vlc_b64_decode_binary( uint8_t **pp_dst
, const char *psz_src
)
449 const int i_src
= strlen( psz_src
);
452 *pp_dst
= p_dst
= malloc( i_src
);
455 return vlc_b64_decode_binary_to_buffer( p_dst
, i_src
, psz_src
);
457 char *vlc_b64_decode( const char *psz_src
)
459 const int i_src
= strlen( psz_src
);
460 char *p_dst
= malloc( i_src
+ 1 );
465 i_dst
= vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst
, i_src
, psz_src
);
471 char *vlc_strftime( const char *tformat
)
476 if (strcmp (tformat
, "") == 0)
477 return strdup (""); /* corner case w.r.t. strftime() return value */
479 /* Get the current time. */
482 /* Convert it to local time representation. */
483 localtime_r( &curtime
, &loctime
);
484 for (size_t buflen
= strlen (tformat
) + 32;; buflen
+= 32)
486 char *str
= malloc (buflen
);
490 size_t len
= strftime (str
, buflen
, tformat
, &loctime
);
493 char *ret
= realloc (str
, len
+ 1);
494 return ret
? ret
: str
; /* <- this cannot fail */
498 vlc_assert_unreachable ();
501 static void write_duration(FILE *stream
, int64_t duration
)
506 duration
/= CLOCK_FREQ
;
507 d
= lldiv(duration
, 60);
509 d
= lldiv(d
.quot
, 60);
510 fprintf(stream
, "%02lld:%02lld:%02lld", d
.quot
, d
.rem
, sec
);
513 static int write_meta(FILE *stream
, input_item_t
*item
, vlc_meta_type_t type
)
518 char *value
= input_item_GetMeta(item
, type
);
522 int ret
= fputs(value
, stream
);
527 char *vlc_strfinput(input_thread_t
*input
, const char *s
)
531 #ifdef HAVE_OPEN_MEMSTREAM
532 FILE *stream
= open_memstream(&str
, &len
);
533 #elif defined( _WIN32 )
534 FILE *stream
= vlc_win32_tmpfile();
536 FILE *stream
= tmpfile();
541 input_item_t
*item
= (input
!= NULL
) ? input_GetItem(input
) : NULL
;
544 bool b_is_format
= false;
545 bool b_empty_if_na
= false;
549 while ((c
= *s
) != '\0')
558 b_empty_if_na
= false;
571 write_meta(stream
, item
, vlc_meta_Artist
);
574 write_meta(stream
, item
, vlc_meta_Album
);
577 write_meta(stream
, item
, vlc_meta_Copyright
);
580 write_meta(stream
, item
, vlc_meta_Description
);
583 write_meta(stream
, item
, vlc_meta_EncodedBy
);
586 if (item
!= NULL
&& item
->p_stats
!= NULL
)
588 vlc_mutex_lock(&item
->p_stats
->lock
);
589 fprintf(stream
, "%"PRIi64
,
590 item
->p_stats
->i_displayed_pictures
);
591 vlc_mutex_unlock(&item
->p_stats
->lock
);
593 else if (!b_empty_if_na
)
597 write_meta(stream
, item
, vlc_meta_Genre
);
600 write_meta(stream
, item
, vlc_meta_Language
);
603 write_meta(stream
, item
, vlc_meta_TrackNumber
);
606 write_meta(stream
, item
, vlc_meta_TrackTotal
);
612 char *value
= input_item_GetNowPlayingFb(item
);
616 fputs(value
, stream
);
621 write_meta(stream
, item
, vlc_meta_Rating
);
628 lang
= var_GetNonEmptyString(input
, "sub-language");
634 else if (!b_empty_if_na
)
639 write_meta(stream
, item
, vlc_meta_Title
);
642 write_meta(stream
, item
, vlc_meta_URL
);
645 write_meta(stream
, item
, vlc_meta_Date
);
649 fprintf(stream
, "%"PRId64
,
650 var_GetInteger(input
, "bit-rate") / 1000);
651 else if (!b_empty_if_na
)
656 fprintf(stream
, "%"PRId64
,
657 var_GetInteger(input
, "chapter"));
658 else if (!b_empty_if_na
)
663 write_duration(stream
, input_item_GetDuration(item
));
664 else if (!b_empty_if_na
)
665 fputs("--:--:--", stream
);
670 char *uri
= input_item_GetURI(item
);
680 fprintf(stream
, "%"PRId64
, var_GetInteger(input
, "title"));
681 else if (!b_empty_if_na
)
687 assert(input
!= NULL
);
688 write_duration(stream
, input_item_GetDuration(item
)
689 - var_GetInteger(input
, "time"));
691 else if (!b_empty_if_na
)
692 fputs("--:--:--", stream
);
697 char *name
= input_item_GetName(item
);
710 lang
= var_GetNonEmptyString(input
, "audio-language");
716 else if (!b_empty_if_na
)
722 fprintf(stream
, "%2.1f",
723 var_GetFloat(input
, "position") * 100.f
);
724 else if (!b_empty_if_na
)
725 fputs("--.-%", stream
);
729 fprintf(stream
, "%.3f", var_GetFloat(input
, "rate"));
730 else if (!b_empty_if_na
)
736 int rate
= var_GetInteger(input
, "sample-rate");
737 div_t dr
= div((rate
+ 50) / 100, 10);
739 fprintf(stream
, "%d.%01d", dr
.quot
, dr
.rem
);
741 else if (!b_empty_if_na
)
746 write_duration(stream
, var_GetInteger(input
, "time"));
747 else if (!b_empty_if_na
)
748 fputs("--:--:--", stream
);
751 write_meta(stream
, item
, vlc_meta_Publisher
);
759 audio_output_t
*aout
= input_GetAout(input
);
762 vol
= aout_VolumeGet(aout
);
763 vlc_object_release(aout
);
767 fprintf(stream
, "%ld", lroundf(vol
* 256.f
));
768 else if (!b_empty_if_na
)
769 fputs("---", stream
);
779 char *value
= input_item_GetNowPlayingFb(item
);
782 fputs(value
, stream
);
787 char *title
= input_item_GetTitleFbName(item
);
789 if (write_meta(stream
, item
, vlc_meta_Artist
) >= 0
791 fputs(" - ", stream
);
795 fputs(title
, stream
);
802 b_empty_if_na
= true;
811 #ifdef HAVE_OPEN_MEMSTREAM
812 return (fclose(stream
) == 0) ? str
: NULL
;
815 if (len
!= (size_t)-1 && len
!= SIZE_MAX
)
818 str
= malloc(len
+ 1);
821 fread(str
, len
, 1, stream
);
831 * Sanitize a file name.
833 * Remove forbidden, potentially forbidden and otherwise evil characters from
834 * file names. That includes slashes, and popular characters like colon
837 * \warning This function should only be used for automatically generated
838 * file names. Do not use this on full paths, only single file names without
839 * any directory separator!
841 void filename_sanitize( char *str
)
845 /* Special file names, not allowed */
846 if( !strcmp( str
, "." ) || !strcmp( str
, ".." ) )
853 /* On platforms not using UTF-8, VLC cannot access non-Unicode paths.
854 * Also, some file systems require Unicode file names.
855 * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
858 /* Avoid leading spaces to please Windows. */
859 while( (c
= *str
) != '\0' )
868 while( (c
= *str
) != '\0' )
870 /* Non-printable characters are not a good idea */
873 /* This is the list of characters not allowed by Microsoft.
874 * We also black-list them on Unix as they may be confusing, and are
875 * not supported by some file system types (notably CIFS). */
876 else if( strchr( "/:\\*\"?|<>", c
) != NULL
)
881 /* Avoid trailing spaces also to please Windows. */
884 if( *(--str
) != ' ' )