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>
46 #include <vlc_memstream.h>
48 #include <vlc_strings.h>
49 #include <vlc_charset.h>
50 #include <vlc_arrays.h>
54 static const struct xml_entity_s
59 /* Important: this list has to be in alphabetical order (psz_entity-wise) */
141 { "nbsp;", "\xc2\xa0" },
186 static int cmp_entity (const void *key
, const void *elem
)
188 const struct xml_entity_s
*ent
= elem
;
189 const char *name
= key
;
191 return strncmp (name
, ent
->psz_entity
, strlen (ent
->psz_entity
));
194 void vlc_xml_decode( char *psz_value
)
196 char *p_pos
= psz_value
;
200 if( *psz_value
== '&' )
202 if( psz_value
[1] == '#' )
203 { /* &#DDD; or &#xHHHH; Unicode code point */
207 if( psz_value
[2] == 'x' ) /* The x must be lower-case. */
208 cp
= strtoul( psz_value
+ 3, &psz_end
, 16 );
210 cp
= strtoul( psz_value
+ 2, &psz_end
, 10 );
212 if( *psz_end
== ';' )
214 psz_value
= psz_end
+ 1;
216 (void)0; /* skip nulls */
223 /* Unicode code point outside ASCII.
224 * &#xxx; representation is longer than UTF-8 :) */
227 *p_pos
++ = 0xC0 | (cp
>> 6);
228 *p_pos
= 0x80 | (cp
& 0x3F);
233 *p_pos
++ = 0xE0 | (cp
>> 12);
234 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
235 *p_pos
= 0x80 | (cp
& 0x3F);
238 if( cp
<= 0x1FFFFF ) /* Outside the BMP */
239 { /* Unicode stops at 10FFFF, but who cares? */
240 *p_pos
++ = 0xF0 | (cp
>> 18);
241 *p_pos
++ = 0x80 | ((cp
>> 12) & 0x3F);
242 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
243 *p_pos
= 0x80 | (cp
& 0x3F);
248 /* Invalid entity number */
254 { /* Well-known XML entity */
255 const struct xml_entity_s
*ent
;
257 ent
= bsearch (psz_value
+ 1, xml_entities
,
258 ARRAY_SIZE (xml_entities
),
259 sizeof (*ent
), cmp_entity
);
262 size_t olen
= strlen (ent
->psz_char
);
263 memcpy (p_pos
, ent
->psz_char
, olen
);
265 psz_value
+= strlen (ent
->psz_entity
) + 1;
286 char *vlc_xml_encode (const char *str
)
288 struct vlc_memstream stream
;
293 vlc_memstream_open(&stream
);
295 while ((n
= vlc_towc (str
, &cp
)) != 0)
297 if (unlikely(n
== (size_t)-1))
299 if (vlc_memstream_close(&stream
) == 0)
308 vlc_memstream_puts(&stream
, """);
311 vlc_memstream_puts(&stream
, "&");
314 vlc_memstream_puts(&stream
, "'");
317 vlc_memstream_puts(&stream
, "<");
320 vlc_memstream_puts(&stream
, ">");
323 if (cp
< 32) /* C0 code not allowed (except 9, 10 and 13) */
325 if (cp
>= 128 && cp
< 160) /* C1 code encoded (except 133) */
327 vlc_memstream_printf(&stream
, "&#%"PRIu32
";", cp
);
335 vlc_memstream_write(&stream
, str
, n
);
341 if (vlc_memstream_close(&stream
))
346 /* Base64 encoding */
347 char *vlc_b64_encode_binary( const uint8_t *src
, size_t i_src
)
349 static const char b64
[] =
350 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
352 char *ret
= malloc( ( i_src
+ 4 ) * 4 / 3 );
360 /* pops (up to) 3 bytes of input, push 4 bytes */
364 v
= ((unsigned)*src
++) << 24;
365 *dst
++ = b64
[v
>> 26];
371 *dst
++ = b64
[v
>> 26];
376 v
|= *src
++ << 20; // 3/3
377 *dst
++ = ( i_src
>= 2 ) ? b64
[v
>> 26] : '='; // 3/4
381 *dst
++ = ( i_src
>= 3 ) ? b64
[v
>> 26] : '='; // 4/4
393 char *vlc_b64_encode( const char *src
)
396 return vlc_b64_encode_binary( (const uint8_t*)src
, strlen(src
) );
398 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
401 /* Base64 decoding */
402 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst
, size_t i_dst
, const char *p_src
)
404 static const int b64
[256] = {
405 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
406 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
407 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
408 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
409 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
410 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
411 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
412 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
413 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
414 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
415 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
416 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
417 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
418 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
419 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
420 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
422 uint8_t *p_start
= p_dst
;
423 uint8_t *p
= (uint8_t *)p_src
;
428 for( i_level
= 0, i_last
= 0; (size_t)( p_dst
- p_start
) < i_dst
&& *p
!= '\0'; p
++ )
430 const int c
= b64
[(unsigned int)*p
];
440 *p_dst
++ = ( i_last
<< 2 ) | ( ( c
>> 4)&0x03 );
444 *p_dst
++ = ( ( i_last
<< 4 )&0xf0 ) | ( ( c
>> 2 )&0x0f );
448 *p_dst
++ = ( ( i_last
&0x03 ) << 6 ) | c
;
454 return p_dst
- p_start
;
456 size_t vlc_b64_decode_binary( uint8_t **pp_dst
, const char *psz_src
)
458 const int i_src
= strlen( psz_src
);
461 *pp_dst
= p_dst
= malloc( i_src
);
464 return vlc_b64_decode_binary_to_buffer( p_dst
, i_src
, psz_src
);
466 char *vlc_b64_decode( const char *psz_src
)
468 const int i_src
= strlen( psz_src
);
469 char *p_dst
= malloc( i_src
+ 1 );
474 i_dst
= vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst
, i_src
, psz_src
);
480 char *vlc_strftime( const char *tformat
)
485 if (strcmp (tformat
, "") == 0)
486 return strdup (""); /* corner case w.r.t. strftime() return value */
488 /* Get the current time. */
491 /* Convert it to local time representation. */
492 localtime_r( &curtime
, &loctime
);
493 for (size_t buflen
= strlen (tformat
) + 32;; buflen
+= 32)
495 char *str
= malloc (buflen
);
499 size_t len
= strftime (str
, buflen
, tformat
, &loctime
);
502 char *ret
= realloc (str
, len
+ 1);
503 return ret
? ret
: str
; /* <- this cannot fail */
507 vlc_assert_unreachable ();
510 static void write_duration(struct vlc_memstream
*stream
, int64_t duration
)
515 duration
/= CLOCK_FREQ
;
516 d
= lldiv(duration
, 60);
518 d
= lldiv(d
.quot
, 60);
519 vlc_memstream_printf(stream
, "%02lld:%02lld:%02lld", d
.quot
, d
.rem
, sec
);
522 static int write_meta(struct vlc_memstream
*stream
, input_item_t
*item
,
523 vlc_meta_type_t type
)
528 char *value
= input_item_GetMeta(item
, type
);
532 vlc_memstream_puts(stream
, value
);
537 char *vlc_strfinput(input_thread_t
*input
, const char *s
)
539 struct vlc_memstream stream
[1];
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 vlc_memstream_open(stream
);
551 while ((c
= *s
) != '\0')
560 b_empty_if_na
= false;
564 vlc_memstream_putc(stream
, c
);
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 vlc_memstream_printf(stream
, "%"PRIi64
,
592 item
->p_stats
->i_displayed_pictures
);
593 vlc_mutex_unlock(&item
->p_stats
->lock
);
595 else if (!b_empty_if_na
)
596 vlc_memstream_putc(stream
, '-');
599 write_meta(stream
, item
, vlc_meta_Genre
);
602 write_meta(stream
, item
, vlc_meta_Language
);
605 write_meta(stream
, item
, vlc_meta_TrackNumber
);
608 write_meta(stream
, item
, vlc_meta_TrackTotal
);
614 char *value
= input_item_GetNowPlayingFb(item
);
618 vlc_memstream_puts(stream
, value
);
623 write_meta(stream
, item
, vlc_meta_Rating
);
630 lang
= var_GetNonEmptyString(input
, "sub-language");
633 vlc_memstream_puts(stream
, lang
);
636 else if (!b_empty_if_na
)
637 vlc_memstream_putc(stream
, '-');
641 write_meta(stream
, item
, vlc_meta_Title
);
644 write_meta(stream
, item
, vlc_meta_URL
);
647 write_meta(stream
, item
, vlc_meta_Date
);
651 vlc_memstream_printf(stream
, "%"PRId64
,
652 var_GetInteger(input
, "bit-rate") / 1000);
653 else if (!b_empty_if_na
)
654 vlc_memstream_putc(stream
, '-');
658 vlc_memstream_printf(stream
, "%"PRId64
,
659 var_GetInteger(input
, "chapter"));
660 else if (!b_empty_if_na
)
661 vlc_memstream_putc(stream
, '-');
665 write_duration(stream
, input_item_GetDuration(item
));
666 else if (!b_empty_if_na
)
667 vlc_memstream_puts(stream
, "--:--:--");
672 char *uri
= input_item_GetURI(item
);
675 vlc_memstream_puts(stream
, uri
);
682 vlc_memstream_printf(stream
, "%"PRId64
,
683 var_GetInteger(input
, "title"));
684 else if (!b_empty_if_na
)
685 vlc_memstream_putc(stream
, '-');
690 assert(input
!= NULL
);
691 write_duration(stream
, input_item_GetDuration(item
)
692 - var_GetInteger(input
, "time"));
694 else if (!b_empty_if_na
)
695 vlc_memstream_puts(stream
, "--:--:--");
700 char *name
= input_item_GetName(item
);
703 vlc_memstream_puts(stream
, name
);
713 lang
= var_GetNonEmptyString(input
, "audio-language");
716 vlc_memstream_puts(stream
, lang
);
719 else if (!b_empty_if_na
)
720 vlc_memstream_putc(stream
, '-');
725 vlc_memstream_printf(stream
, "%2.1f",
726 var_GetFloat(input
, "position") * 100.f
);
727 else if (!b_empty_if_na
)
728 vlc_memstream_puts(stream
, "--.-%");
732 vlc_memstream_printf(stream
, "%.3f",
733 var_GetFloat(input
, "rate"));
734 else if (!b_empty_if_na
)
735 vlc_memstream_putc(stream
, '-');
740 int rate
= var_GetInteger(input
, "sample-rate");
741 div_t dr
= div((rate
+ 50) / 100, 10);
743 vlc_memstream_printf(stream
, "%d.%01d", dr
.quot
, dr
.rem
);
745 else if (!b_empty_if_na
)
746 vlc_memstream_putc(stream
, '-');
750 write_duration(stream
, var_GetInteger(input
, "time"));
751 else if (!b_empty_if_na
)
752 vlc_memstream_puts(stream
, "--:--:--");
755 write_meta(stream
, item
, vlc_meta_Publisher
);
763 audio_output_t
*aout
= input_GetAout(input
);
766 vol
= aout_VolumeGet(aout
);
767 vlc_object_release(aout
);
771 vlc_memstream_printf(stream
, "%ld", lroundf(vol
* 256.f
));
772 else if (!b_empty_if_na
)
773 vlc_memstream_puts(stream
, "---");
777 vlc_memstream_putc(stream
, '\n');
783 char *value
= input_item_GetNowPlayingFb(item
);
786 vlc_memstream_puts(stream
, value
);
791 char *title
= input_item_GetTitleFbName(item
);
793 if (write_meta(stream
, item
, vlc_meta_Artist
) >= 0
795 vlc_memstream_puts(stream
, " - ");
799 vlc_memstream_puts(stream
, title
);
806 b_empty_if_na
= true;
810 vlc_memstream_putc(stream
, c
);
815 if (vlc_memstream_close(stream
))
821 * Sanitize a file name.
823 * Remove forbidden, potentially forbidden and otherwise evil characters from
824 * file names. That includes slashes, and popular characters like colon
827 * \warning This function should only be used for automatically generated
828 * file names. Do not use this on full paths, only single file names without
829 * any directory separator!
831 void filename_sanitize( char *str
)
835 /* Special file names, not allowed */
836 if( !strcmp( str
, "." ) || !strcmp( str
, ".." ) )
843 /* On platforms not using UTF-8, VLC cannot access non-Unicode paths.
844 * Also, some file systems require Unicode file names.
845 * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */
848 /* Avoid leading spaces to please Windows. */
849 while( (c
= *str
) != '\0' )
858 while( (c
= *str
) != '\0' )
860 /* Non-printable characters are not a good idea */
863 /* This is the list of characters not allowed by Microsoft.
864 * We also black-list them on Unix as they may be confusing, and are
865 * not supported by some file system types (notably CIFS). */
866 else if( strchr( "/:\\*\"?|<>", c
) != NULL
)
871 /* Avoid trailing spaces also to please Windows. */
874 if( *(--str
) != ' ' )