X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Ftext%2Fstrings.c;h=04005bc124857e3ecdf7fddb741111a04967cd9f;hb=14f739a56a0515da2905eb7f591fe9ad55f4a040;hp=8ae50a508985b65be0c2ae0195203b090d47ab75;hpb=b450808821f4dac09796fbcba2a100b6c5ef0e77;p=vlc diff --git a/src/text/strings.c b/src/text/strings.c index 8ae50a5089..04005bc124 100644 --- a/src/text/strings.c +++ b/src/text/strings.c @@ -1,7 +1,7 @@ /***************************************************************************** * strings.c: String related functions ***************************************************************************** - * Copyright (C) 2006 the VideoLAN team + * Copyright (C) 2006 VLC authors and VideoLAN * Copyright (C) 2008-2009 Rémi Denis-Courmont * $Id$ * @@ -9,19 +9,19 @@ * Daniel Stranger * Rémi Denis-Courmont * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -37,133 +37,17 @@ /* Needed by str_format_time */ #include #include +#include /* Needed by str_format_meta */ #include #include -#include #include #include -#include #include - -/** - * Decode encoded URI component. See also decode_URI(). - * \return decoded duplicated string - */ -char *decode_URI_duplicate( const char *psz ) -{ - char *psz_dup = strdup( psz ); - decode_URI( psz_dup ); - return psz_dup; -} - -/** - * Decode an encoded URI component in place. - * This function does NOT decode entire URIs. - * It decodes components (e.g. host name, directory, file name). - * Decoded URIs do not exist in the real world (see RFC3986 §2.4). - * Complete URIs are always "encoded" (or they are syntaxically invalid). - * - * Note that URI encoding is different from Javascript escaping. Especially, - * white spaces and Unicode non-ASCII code points are encoded differently. - * - * \return psz on success, NULL if it was not properly encoded - */ -char *decode_URI( char *psz ) -{ - unsigned char *in = (unsigned char *)psz, *out = in, c; - - if( psz == NULL ) - return NULL; - - while( ( c = *in++ ) != '\0' ) - { - switch( c ) - { - case '%': - { - char hex[3]; - - if( ( ( hex[0] = *in++ ) == 0 ) - || ( ( hex[1] = *in++ ) == 0 ) ) - return NULL; - - hex[2] = '\0'; - *out++ = (unsigned char)strtoul( hex, NULL, 0x10 ); - break; - } - - case '+': /* This is HTTP forms, not URI decoding... */ - *out++ = ' '; - break; - - default: - /* Inserting non-ASCII or non-printable characters is unsafe, - * and no sane browser will send these unencoded */ - if( ( c < 32 ) || ( c > 127 ) ) - *out++ = '?'; - else - *out++ = c; - } - } - *out = '\0'; - return psz; -} - -static inline bool isurisafe( int c ) -{ - /* These are the _unreserved_ URI characters (RFC3986 §2.3) */ - return ( (unsigned char)( c - 'a' ) < 26 ) - || ( (unsigned char)( c - 'A' ) < 26 ) - || ( (unsigned char)( c - '0' ) < 10 ) - || ( strchr( "-._~", c ) != NULL ); -} - -static char *encode_URI_bytes (const char *psz_uri, size_t len) -{ - char *psz_enc = malloc (3 * len + 1), *out = psz_enc; - if (psz_enc == NULL) - return NULL; - - for (size_t i = 0; i < len; i++) - { - static const char hex[16] = "0123456789ABCDEF"; - uint8_t c = *psz_uri; - - if( isurisafe( c ) ) - *out++ = c; - /* This is URI encoding, not HTTP forms: - * Space is encoded as '%20', not '+'. */ - else - { - *out++ = '%'; - *out++ = hex[c >> 4]; - *out++ = hex[c & 0xf]; - } - psz_uri++; - } - *out++ = '\0'; - - out = realloc (psz_enc, out - psz_enc); - return out ? out : psz_enc; /* realloc() can fail (safe) */ -} - -/** - * Encodes an URI component (RFC3986 §2). - * - * @param psz_uri nul-terminated UTF-8 representation of the component. - * Obviously, you can't pass an URI containing a nul character, but you don't - * want to do that, do you? - * - * @return encoded string (must be free()'d), or NULL for ENOMEM. - */ -char *encode_URI_component( const char *psz_uri ) -{ - return encode_URI_bytes (psz_uri, strlen (psz_uri)); -} - +#include +#include static const struct xml_entity_s { @@ -318,9 +202,15 @@ void resolve_xml_special_chars( char *psz_value ) if( *psz_value == '&' ) { if( psz_value[1] == '#' ) - { /* &#xxx; Unicode code point */ + { /* &#DDD; or &#xHHHH; Unicode code point */ char *psz_end; - unsigned long cp = strtoul( psz_value+2, &psz_end, 10 ); + unsigned long cp; + + if( psz_value[2] == 'x' ) /* The x must be lower-case. */ + cp = strtoul( psz_value + 3, &psz_end, 16 ); + else + cp = strtoul( psz_value + 2, &psz_end, 10 ); + if( *psz_end == ';' ) { psz_value = psz_end + 1; @@ -396,41 +286,51 @@ void resolve_xml_special_chars( char *psz_value ) } /** - * Converts '<', '>', '\"', '\'' and '&' to their html entities - * \param psz_content simple element content that is to be converted + * XML-encode an UTF-8 string + * \param str nul-terminated UTF-8 byte sequence to XML-encode + * \return XML encoded string or NULL on error + * (errno is set to ENOMEM or EILSEQ as appropriate) */ -char *convert_xml_special_chars( const char *psz_content ) +char *convert_xml_special_chars (const char *str) { - assert( psz_content ); - - const size_t len = strlen( psz_content ); - char *const psz_temp = malloc( 6 * len + 1 ); - char *p_to = psz_temp; + assert (str != NULL); - if( psz_temp == NULL ) + const size_t len = strlen (str); + char *const buf = malloc (6 * len + 1), *ptr = buf; + if (unlikely(buf == NULL)) return NULL; - for( size_t i = 0; i < len; i++ ) + + size_t n; + uint32_t cp; + + while ((n = vlc_towc (str, &cp)) != 0) { - const char *str; - char c = psz_content[i]; + if (unlikely(n == (size_t)-1)) + { + free (buf); + errno = EILSEQ; + return NULL; + } - switch ( c ) + if ((cp & ~0x0080) < 32 /* C0/C1 control codes */ + && memchr ("\x09\x0A\x0D\x85", cp, 4) == NULL) + ptr += sprintf (ptr, "&#%"PRIu32";", cp); + else + switch (cp) { - case '\"': str = "quot"; break; - case '&': str = "amp"; break; - case '\'': str = "#39"; break; - case '<': str = "lt"; break; - case '>': str = "gt"; break; - default: - *(p_to++) = c; - continue; + case '\"': strcpy (ptr, """); ptr += 6; break; + case '&': strcpy (ptr, "&"); ptr += 5; break; + case '\'': strcpy (ptr, "'"); ptr += 5; break; + case '<': strcpy (ptr, "<"); ptr += 4; break; + case '>': strcpy (ptr, ">"); ptr += 4; break; + default: memcpy (ptr, str, n); ptr += n; break; } - p_to += sprintf( p_to, "&%s;", str ); + str += n; } - *(p_to++) = '\0'; + *(ptr++) = '\0'; - p_to = realloc( psz_temp, p_to - psz_temp ); - return p_to ? p_to : psz_temp; /* cannot fail */ + ptr = realloc (buf, ptr - buf); + return likely(ptr != NULL) ? ptr : buf; /* cannot fail */ } /* Base64 encoding */ @@ -519,7 +419,7 @@ size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst, size_t i_dst, const char { const int c = b64[(unsigned int)*p]; if( c == -1 ) - continue; + break; switch( i_level ) { @@ -597,424 +497,391 @@ char *str_format_time( const char *tformat ) char *ret = realloc (str, len + 1); return ret ? ret : str; /* <- this cannot fail */ } + free (str); } - assert (0); + vlc_assert_unreachable (); } -#define INSERT_STRING( string ) \ - if( string != NULL ) \ - { \ - int len = strlen( string ); \ - dst = xrealloc( dst, i_size = i_size + len );\ - memcpy( (dst+d), string, len ); \ - d += len; \ - free( string ); \ - } \ - else if( !b_empty_if_na ) \ - { \ - *(dst+d) = '-'; \ - d++; \ - } \ - -/* same than INSERT_STRING, except that string won't be freed */ -#define INSERT_STRING_NO_FREE( string ) \ - { \ - int len = strlen( string ); \ - dst = xrealloc( dst, i_size = i_size + len );\ - memcpy( dst+d, string, len ); \ - d += len; \ - } -#undef str_format_meta -char *str_format_meta( vlc_object_t *p_object, const char *string ) +static void write_duration(FILE *stream, int64_t duration) { - const char *s = string; + lldiv_t d; + long long sec; + + duration /= CLOCK_FREQ; + d = lldiv(duration, 60); + sec = d.rem; + d = lldiv(d.quot, 60); + fprintf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec); +} + +static int write_meta(FILE *stream, input_item_t *item, vlc_meta_type_t type) +{ + if (item == NULL) + return EOF; + + char *value = input_item_GetMeta(item, type); + if (value == NULL) + return EOF; + + int ret = fputs(value, stream); + free(value); + return ret; +} + +char *str_format_meta(input_thread_t *input, const char *s) +{ + char *str; + size_t len; +#ifdef HAVE_OPEN_MEMSTREAM + FILE *stream = open_memstream(&str, &len); +#elif defined( _WIN32 ) + FILE *stream = vlc_win32_tmpfile(); + str = NULL; +#else + FILE *stream = tmpfile(); + str = NULL; +#endif + if (stream == NULL) + return NULL; + + input_item_t *item = (input != NULL) ? input_GetItem(input) : NULL; + + char c; bool b_is_format = false; bool b_empty_if_na = false; - char buf[10]; - int i_size = strlen( string ) + 1; /* +1 to store '\0' */ - char *dst = strdup( string ); - if( !dst ) return NULL; - int d = 0; - - input_thread_t *p_input = playlist_CurrentInput( pl_Get(p_object) ); - input_item_t *p_item = NULL; - if( p_input ) - { - p_item = input_GetItem(p_input); - } - while( *s ) + while ((c = *s) != '\0') { - if( b_is_format ) + s++; + + if (!b_is_format) { - switch( *s ) + if (c == '$') { - case 'a': - if( p_item ) - { - INSERT_STRING( input_item_GetArtist( p_item ) ); - } - break; - case 'b': - if( p_item ) - { - INSERT_STRING( input_item_GetAlbum( p_item ) ); - } - break; - case 'c': - if( p_item ) - { - INSERT_STRING( input_item_GetCopyright( p_item ) ); - } - break; - case 'd': - if( p_item ) - { - INSERT_STRING( input_item_GetDescription( p_item ) ); - } - break; - case 'e': - if( p_item ) - { - INSERT_STRING( input_item_GetEncodedBy( p_item ) ); - } - break; - case 'f': - if( p_item && p_item->p_stats ) - { - vlc_mutex_lock( &p_item->p_stats->lock ); - snprintf( buf, 10, "%d", - p_item->p_stats->i_displayed_pictures ); - vlc_mutex_unlock( &p_item->p_stats->lock ); - } - else - { - sprintf( buf, b_empty_if_na ? "" : "-" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'g': - if( p_item ) - { - INSERT_STRING( input_item_GetGenre( p_item ) ); - } - break; - case 'l': - if( p_item ) - { - INSERT_STRING( input_item_GetLanguage( p_item ) ); - } - break; - case 'n': - if( p_item ) - { - INSERT_STRING( input_item_GetTrackNum( p_item ) ); - } - break; - case 'p': - if( p_item ) - { - INSERT_STRING( input_item_GetNowPlaying( p_item ) ); - } - break; - case 'r': - if( p_item ) - { - INSERT_STRING( input_item_GetRating( p_item ) ); - } - break; - case 's': + b_is_format = true; + b_empty_if_na = false; + continue; + } + + fputc(c, stream); + continue; + } + + b_is_format = false; + + switch (c) + { + case 'a': + write_meta(stream, item, vlc_meta_Artist); + break; + case 'b': + write_meta(stream, item, vlc_meta_Album); + break; + case 'c': + write_meta(stream, item, vlc_meta_Copyright); + break; + case 'd': + write_meta(stream, item, vlc_meta_Description); + break; + case 'e': + write_meta(stream, item, vlc_meta_EncodedBy); + break; + case 'f': + if (item != NULL && item->p_stats != NULL) { - char *lang = NULL; - if( p_input ) - lang = var_GetNonEmptyString( p_input, "sub-language" ); - if( lang == NULL ) - lang = strdup( b_empty_if_na ? "" : "-" ); - INSERT_STRING( lang ); - break; + vlc_mutex_lock(&item->p_stats->lock); + fprintf(stream, "%"PRIi64, + item->p_stats->i_displayed_pictures); + vlc_mutex_unlock(&item->p_stats->lock); } - case 't': - if( p_item ) - { - INSERT_STRING( input_item_GetTitle( p_item ) ); - } - break; - case 'u': - if( p_item ) - { - INSERT_STRING( input_item_GetURL( p_item ) ); - } - break; - case 'A': - if( p_item ) - { - INSERT_STRING( input_item_GetDate( p_item ) ); - } - break; - case 'B': - if( p_input ) - { - snprintf( buf, 10, "%d", - var_GetInteger( p_input, "bit-rate" )/1000 ); - } - else - { - sprintf( buf, b_empty_if_na ? "" : "-" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'C': - if( p_input ) - { - snprintf( buf, 10, "%d", - var_GetInteger( p_input, "chapter" ) ); - } - else - { - sprintf( buf, b_empty_if_na ? "" : "-" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'D': - if( p_item ) - { - mtime_t i_duration = input_item_GetDuration( p_item ); - snprintf( buf, 10, "%02d:%02d:%02d", - (int)(i_duration/(3600000000)), - (int)((i_duration/(60000000))%60), - (int)((i_duration/1000000)%60) ); - } - else - { - snprintf( buf, 10, b_empty_if_na ? "" : "--:--:--" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'F': - if( p_item ) - { - INSERT_STRING( input_item_GetURI( p_item ) ); - } - break; - case 'I': - if( p_input ) - { - snprintf( buf, 10, "%d", - var_GetInteger( p_input, "title" ) ); - } - else - { - sprintf( buf, b_empty_if_na ? "" : "-" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'L': - if( p_item && p_input ) - { - mtime_t i_duration = input_item_GetDuration( p_item ); - int64_t i_time = var_GetTime( p_input, "time" ); - snprintf( buf, 10, "%02d:%02d:%02d", - (int)( ( i_duration - i_time ) / 3600000000 ), - (int)( ( ( i_duration - i_time ) / 60000000 ) % 60 ), - (int)( ( ( i_duration - i_time ) / 1000000 ) % 60 ) ); - } - else - { - snprintf( buf, 10, b_empty_if_na ? "" : "--:--:--" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'N': - if( p_item ) - { - INSERT_STRING( input_item_GetName( p_item ) ); - } + else if (!b_empty_if_na) + fputc('-', stream); + break; + case 'g': + write_meta(stream, item, vlc_meta_Genre); + break; + case 'l': + write_meta(stream, item, vlc_meta_Language); + break; + case 'n': + write_meta(stream, item, vlc_meta_TrackNumber); + break; + case 'p': + if (item == NULL) break; - case 'O': { - char *lang = NULL; - if( p_input ) - lang = var_GetNonEmptyString( p_input, - "audio-language" ); - if( lang == NULL ) - lang = strdup( b_empty_if_na ? "" : "-" ); - INSERT_STRING( lang ); - break; + char *value = input_item_GetNowPlayingFb(item); + if (value == NULL) + break; + + fputs(value, stream); + free(value); } - case 'P': - if( p_input ) - { - snprintf( buf, 10, "%2.1lf", - var_GetFloat( p_input, "position" ) * 100. ); - } - else - { - snprintf( buf, 10, b_empty_if_na ? "" : "--.-%%" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'R': - if( p_input ) - { - float f = var_GetFloat( p_input, "rate" ); - snprintf( buf, 10, "%.3f", f ); - } - else + break; + case 'r': + write_meta(stream, item, vlc_meta_Rating); + break; + case 's': + { + char *lang = NULL; + + if (input != NULL) + lang = var_GetNonEmptyString(input, "sub-language"); + if (lang != NULL) + { + fputs(lang, stream); + free(lang); + } + else if (!b_empty_if_na) + fputc('-', stream); + break; + } + case 't': + write_meta(stream, item, vlc_meta_Title); + break; + case 'u': + write_meta(stream, item, vlc_meta_URL); + break; + case 'A': + write_meta(stream, item, vlc_meta_Date); + break; + case 'B': + if (input != NULL) + fprintf(stream, "%"PRId64, + var_GetInteger(input, "bit-rate") / 1000); + else if (!b_empty_if_na) + fputc('-', stream); + break; + case 'C': + if (input != NULL) + fprintf(stream, "%"PRId64, + var_GetInteger(input, "chapter")); + else if (!b_empty_if_na) + fputc('-', stream); + break; + case 'D': + if (item != NULL) + write_duration(stream, input_item_GetDuration(item)); + else if (!b_empty_if_na) + fputs("--:--:--", stream); + break; + case 'F': + if (item != NULL) + { + char *uri = input_item_GetURI(item); + if (uri != NULL) { - sprintf( buf, b_empty_if_na ? "" : "-" ); + fputs(uri, stream); + free(uri); } - INSERT_STRING_NO_FREE( buf ); - break; - case 'S': - if( p_input ) + } + break; + case 'I': + if (input != NULL) + fprintf(stream, "%"PRId64, var_GetInteger(input, "title")); + else if (!b_empty_if_na) + fputc('-', stream); + break; + case 'L': + if (item != NULL) + { + assert(input != NULL); + write_duration(stream, input_item_GetDuration(item) + - var_GetTime(input, "time")); + } + else if (!b_empty_if_na) + fputs("--:--:--", stream); + break; + case 'N': + if (item != NULL) + { + char *name = input_item_GetName(item); + if (name != NULL) { - int r = var_GetInteger( p_input, "sample-rate" ); - snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 ); + fputs(name, stream); + free(name); } - else + } + break; + case 'O': + { + char *lang = NULL; + + if (input != NULL) + lang = var_GetNonEmptyString(input, "audio-language"); + if (lang != NULL) + { + fputs(lang, stream); + free(lang); + } + else if (!b_empty_if_na) + fputc('-', stream); + break; + } + case 'P': + if (input != NULL) + fprintf(stream, "%2.1f", + var_GetFloat(input, "position") * 100.f); + else if (!b_empty_if_na) + fputs("--.-%", stream); + break; + case 'R': + if (input != NULL) + fprintf(stream, "%.3f", var_GetFloat(input, "rate")); + else if (!b_empty_if_na) + fputc('-', stream); + break; + case 'S': + if (input != NULL) + { + int rate = var_GetInteger(input, "sample-rate"); + div_t dr = div((rate + 50) / 100, 10); + + fprintf(stream, "%d.%01d", dr.quot, dr.rem); + } + else if (!b_empty_if_na) + fputc('-', stream); + break; + case 'T': + if (input != NULL) + write_duration(stream, var_GetTime(input, "time")); + else if (!b_empty_if_na) + fputs("--:--:--", stream); + break; + case 'U': + write_meta(stream, item, vlc_meta_Publisher); + break; + case 'V': + { + float vol = 0.f; + + if (input != NULL) + { + audio_output_t *aout = input_GetAout(input); + if (aout != NULL) { - sprintf( buf, b_empty_if_na ? "" : "-" ); + vol = aout_VolumeGet(aout); + vlc_object_release(aout); } - INSERT_STRING_NO_FREE( buf ); + } + if (vol >= 0.f) + fprintf(stream, "%ld", lroundf(vol * 256.f)); + else if (!b_empty_if_na) + fputs("---", stream); + break; + } + case '_': + fputc('\n', stream); + break; + case 'Z': + if (item == NULL) break; - case 'T': - if( p_input ) + { + char *value = input_item_GetNowPlayingFb(item); + if (value != NULL) { - int64_t i_time = var_GetTime( p_input, "time" ); - snprintf( buf, 10, "%02d:%02d:%02d", - (int)( i_time / ( 3600000000 ) ), - (int)( ( i_time / ( 60000000 ) ) % 60 ), - (int)( ( i_time / 1000000 ) % 60 ) ); + fputs(value, stream); + free(value); } else { - snprintf( buf, 10, b_empty_if_na ? "" : "--:--:--" ); - } - INSERT_STRING_NO_FREE( buf ); - break; - case 'U': - if( p_item ) - { - INSERT_STRING( input_item_GetPublisher( p_item ) ); - } - break; - case 'V': - { - audio_volume_t volume; - aout_VolumeGet( p_object, &volume ); - snprintf( buf, 10, "%d", volume ); - INSERT_STRING_NO_FREE( buf ); - break; - } - case '_': - *(dst+d) = '\n'; - d++; - break; + char *title = input_item_GetTitleFbName(item); - case ' ': - b_empty_if_na = true; - break; + if (write_meta(stream, item, vlc_meta_Artist) >= 0 + && title != NULL) + fputs(" - ", stream); - default: - *(dst+d) = *s; - d++; - break; - } - if( *s != ' ' ) - b_is_format = false; - } - else if( *s == '$' ) - { - b_is_format = true; - b_empty_if_na = false; - } - else - { - *(dst+d) = *s; - d++; + if (title != NULL) + { + fputs(title, stream); + free(title); + } + } + } + break; + case ' ': + b_empty_if_na = true; + b_is_format = true; + break; + default: + fputc(c, stream); + break; } - s++; } - *(dst+d) = '\0'; - if( p_input ) - vlc_object_release( p_input ); - - return dst; -} -#undef INSERT_STRING -#undef INSERT_STRING_NO_FREE - -#undef str_format -/** - * Apply str format time and str format meta - */ -char *str_format( vlc_object_t *p_this, const char *psz_src ) -{ - char *psz_buf1, *psz_buf2; - psz_buf1 = str_format_time( psz_src ); - psz_buf2 = str_format_meta( p_this, psz_buf1 ); - free( psz_buf1 ); - return psz_buf2; +#ifdef HAVE_OPEN_MEMSTREAM + return (fclose(stream) == 0) ? str : NULL; +#else + len = ftell(stream); + if (len != (size_t)-1) + { + rewind(stream); + str = xmalloc(len + 1); + fread(str, len, 1, stream); + str[len] = '\0'; + } + else + str = NULL; + fclose(stream); + return str; +#endif } /** - * Remove forbidden characters from filenames (including slashes) + * Remove forbidden, potentially forbidden and otherwise evil characters from + * filenames. This includes slashes, and popular characters like colon + * (on Unix anyway), so this should only be used for automatically generated + * filenames. + * \warning Do not use this on full paths, + * only single file names without any directory separator! */ void filename_sanitize( char *str ) { -#if defined( WIN32 ) - char *str_base = str; -#endif + unsigned char c; - if( *str == '.' && (str[1] == '\0' || (str[1] == '.' && str[2] == '\0' ) ) ) + /* Special file names, not allowed */ + if( !strcmp( str, "." ) || !strcmp( str, ".." ) ) { while( *str ) - { - *str = '_'; - str++; - } + *(str++) = '_'; return; } -#if defined( WIN32 ) - // Change leading spaces into underscores - while( *str && *str == ' ' ) - *str++ = '_'; -#endif + /* On platforms not using UTF-7, VLC cannot access non-Unicode paths. + * Also, some file systems require Unicode file names. + * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */ + EnsureUTF8( str ); - while( *str ) + /* Avoid leading spaces to please Windows. */ + while( (c = *str) != '\0' ) { - switch( *str ) - { - case '/': -#if defined( __APPLE__ ) - case ':': -#elif defined( WIN32 ) - case '\\': - case '*': - case '"': - case '?': - case ':': - case '|': - case '<': - case '>': -#endif - *str = '_'; - } + if( c != ' ' ) + break; + *(str++) = '_'; + } + + char *start = str; + + while( (c = *str) != '\0' ) + { + /* Non-printable characters are not a good idea */ + if( c < 32 ) + *str = '_'; + /* This is the list of characters not allowed by Microsoft. + * We also black-list them on Unix as they may be confusing, and are + * not supported by some file system types (notably CIFS). */ + else if( strchr( "/:\\*\"?|<>", c ) != NULL ) + *str = '_'; str++; } -#if defined( WIN32 ) - // Change trailing spaces into underscores - str--; - while( str != str_base ) + /* Avoid trailing spaces also to please Windows. */ + while( str > start ) { - if( *str != ' ' ) + if( *(--str) != ' ' ) break; - *str-- = '_'; + *str = '_'; } -#endif } /** @@ -1022,7 +889,7 @@ void filename_sanitize( char *str ) */ void path_sanitize( char *str ) { -#ifdef WIN32 +#if defined( _WIN32 ) || defined( __OS2__ ) /* check drive prefix if path is absolute */ if( (((unsigned char)(str[0] - 'A') < 26) || ((unsigned char)(str[0] - 'a') < 26)) && (':' == str[1]) ) @@ -1033,7 +900,7 @@ void path_sanitize( char *str ) #if defined( __APPLE__ ) if( *str == ':' ) *str = '_'; -#elif defined( WIN32 ) +#elif defined( _WIN32 ) || defined( __OS2__ ) if( strchr( "*\"?:|<>", *str ) ) *str = '_'; if( *str == '/' ) @@ -1043,198 +910,61 @@ void path_sanitize( char *str ) } } -#include +/* + Decodes a duration as defined by ISO 8601 + http://en.wikipedia.org/wiki/ISO_8601#Durations + @param str A null-terminated string to convert + @return: The duration in seconds. -1 if an error occurred. -/** - * Convert a file path to an URI. - * If already an URI, return a copy of the string. - * @path path path to convert (or URI to copy) - * @return a nul-terminated URI string (use free() to release it), - * or NULL in case of error + Exemple input string: "PT0H9M56.46S" */ -char *make_URI (const char *path) +time_t str_duration( const char *psz_duration ) { - if (path == NULL) - return NULL; - if (!strcmp (path, "-")) - return strdup ("fd://0"); // standard input - if (strstr (path, "://") != NULL) - return strdup (path); /* Already an URI */ - /* Note: VLC cannot handle URI schemes without double slash after the - * scheme name (such as mailto: or news:). */ - - char *buf; -#ifdef WIN32 - if (isalpha (path[0]) && (path[1] == ':')) + bool timeDesignatorReached = false; + time_t res = 0; + char* end_ptr; + + if ( psz_duration == NULL ) + return -1; + if ( ( *(psz_duration++) ) != 'P' ) + return -1; + do { - if (asprintf (&buf, "file:///%c:", path[0]) == -1) - buf = NULL; - path += 2; - } - else -#endif - if (!strncmp (path, "\\\\", 2)) - { /* Windows UNC paths */ -#ifndef WIN32 - /* \\host\share\path -> smb://host/share/path */ - if (strchr (path + 2, '\\') != NULL) - { /* Convert backslashes to slashes */ - char *dup = strdup (path); - if (dup == NULL) - return NULL; - for (size_t i = 2; dup[i]; i++) - if (dup[i] == '\\') - dup[i] = DIR_SEP_CHAR; - - char *ret = make_URI (dup); - free (dup); - return ret; - } -# define SMB_SCHEME "smb" -#else - /* \\host\share\path -> file://host/share/path */ -# define SMB_SCHEME "file" -#endif - size_t hostlen = strcspn (path + 2, DIR_SEP); - - buf = malloc (sizeof (SMB_SCHEME) + 3 + hostlen); - if (buf != NULL) - snprintf (buf, sizeof (SMB_SCHEME) + 3 + hostlen, - SMB_SCHEME"://%s", path + 2); - path += 2 + hostlen; - } - else - if (path[0] != DIR_SEP_CHAR) - { /* Relative path: prepend the current working directory */ - char cwd[PATH_MAX]; - - if (getcwd (cwd, sizeof (cwd)) == NULL) /* FIXME: UTF8? */ - return NULL; - if (asprintf (&buf, "%s/%s", cwd, path) == -1) - return NULL; - char *ret = make_URI (buf); - free (buf); - return ret; - } - else - buf = strdup ("file://"); - if (buf == NULL) - return NULL; - - assert (path[0] == DIR_SEP_CHAR); - - /* Absolute file path */ - for (const char *ptr = path + 1;; ptr++) - { - size_t len = strcspn (ptr, DIR_SEP); - char *component = encode_URI_bytes (ptr, len); - if (component == NULL) - { - free (buf); - return NULL; - } - char *uri; - int val = asprintf (&uri, "%s/%s", buf, component); - free (component); - free (buf); - if (val == -1) - return NULL; - buf = uri; - ptr += len; - if (*ptr == '\0') - return buf; - } -} - -/** - * Tries to convert an URI to a local (UTF-8-encoded) file path. - * @param url URI to convert - * @return NULL on error, a nul-terminated string otherwise - * (use free() to release it) - */ -char *make_path (const char *url) -{ - char *ret = NULL; - char *end; - - char *path = strstr (url, "://"); - if (path == NULL) - return NULL; /* unsupported scheme or invalid syntax */ - - end = memchr (url, '/', path - url); - size_t schemelen = ((end != NULL) ? end : path) - url; - path += 3; /* skip "://" */ - - /* Remove HTML anchor if present */ - end = strchr (path, '#'); - if (end) - path = strndup (path, end - path); - else - path = strdup (path); - if (unlikely(path == NULL)) - return NULL; /* boom! */ - - /* Decode path */ - decode_URI (path); - - if (schemelen == 4 && !strncasecmp (url, "file", 4)) - { -#if (DIR_SEP_CHAR != '/') - for (char *p = strchr (path, '/'); p; p = strchr (p + 1, '/')) - *p = DIR_SEP_CHAR; -#endif - /* Leading slash => local path */ - if (*path == DIR_SEP_CHAR) -#if !defined (WIN32) || defined (UNDER_CE) - return path; -#else - return memmove (path, path + 1, strlen (path + 1) + 1); -#endif - - /* Local path disguised as a remote one (MacOS X) */ - if (!strncasecmp (path, "localhost"DIR_SEP, 10)) - return memmove (path, path + 9, strlen (path + 9) + 1); - -#ifdef WIN32 - if (*path && asprintf (&ret, "\\\\%s", path) == -1) - ret = NULL; -#endif - /* non-local path :-( */ - } - else - if (schemelen == 2 && !strncasecmp (url, "fd", 2)) - { - int fd = strtol (path, &end, 0); - - if (*end) - goto out; - -#ifndef WIN32 - switch (fd) + double number = us_strtod( psz_duration, &end_ptr ); + double mul = 0; + if ( psz_duration != end_ptr ) + psz_duration = end_ptr; + switch( *psz_duration ) { - case 0: - ret = strdup ("/dev/stdin"); - break; - case 1: - ret = strdup ("/dev/stdout"); - break; - case 2: - ret = strdup ("/dev/stderr"); - break; + case 'M': + { + //M can mean month or minutes, if the 'T' flag has been reached. + //We don't handle months though. + if ( timeDesignatorReached == true ) + mul = 60.0; + break ; + } + case 'Y': + case 'W': + break ; //Don't handle this duration. + case 'D': + mul = 86400.0; + break ; + case 'T': + timeDesignatorReached = true; + break ; + case 'H': + mul = 3600.0; + break ; + case 'S': + mul = 1.0; + break ; default: - if (asprintf (&ret, "/dev/fd/%d", fd) == -1) - ret = NULL; + break ; } -#else - /* XXX: Does this work on WinCE? */ - if (fd < 2) - ret = strdup ("CON"); - else - ret = NULL; -#endif - } - -out: - free (path); - return ret; /* unknown scheme */ + res += (time_t)(mul * number); + if ( *psz_duration ) + psz_duration++; + } while ( *psz_duration ); + return res; }