#include <stdlib.h>
#include <assert.h>
+/* Needed by str_format_time */
+#include <time.h>
+
+/* Needed by str_format_meta */
+#include "vlc_input.h"
+#include "vlc_meta.h"
+#include "vlc_playlist.h"
+#include <vlc/aout.h>
+
#include "vlc_strings.h"
+#include "vlc_url.h"
+#include "charset.h"
/**
- * Decode URI encoded string
+ * Unescape URI encoded string
* \return decoded duplicated string
*/
-char *decode_encoded_URI_duplicate( const char *psz )
+char *unescape_URI_duplicate( const char *psz )
{
char *psz_dup = strdup( psz );
- decode_encoded_URI( psz_dup );
+ unescape_URI( psz_dup );
return psz_dup;
}
/**
- * Decode URI encoded string
+ * Unescape URI encoded string in place
* \return nothing
*/
-void decode_encoded_URI( char *psz )
+void unescape_URI( char *psz )
{
unsigned char *in = (unsigned char *)psz, *out = in, c;
break;
}
+ /* + is not a special case - it means plus, not space. */
+
+ 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';
+}
+
+/**
+ * Decode encoded URI string
+ * \return decoded duplicated string
+ */
+char *decode_URI_duplicate( const char *psz )
+{
+ char *psz_dup = strdup( psz );
+ decode_URI( psz_dup );
+ return psz_dup;
+}
+
+/**
+ * Decode encoded URI string in place
+ * \return nothing
+ */
+void decode_URI( char *psz )
+{
+ unsigned char *in = (unsigned char *)psz, *out = in, c;
+
+ while( ( c = *in++ ) != '\0' )
+ {
+ switch( c )
+ {
+ case '%':
+ {
+ char hex[3];
+
+ if( ( ( hex[0] = *in++ ) == 0 )
+ || ( ( hex[1] = *in++ ) == 0 ) )
+ return;
+
+ hex[2] = '\0';
+ *out++ = (unsigned char)strtoul( hex, NULL, 0x10 );
+ break;
+ }
+
case '+':
*out++ = ' ';
break;
}
}
*out = '\0';
+ EnsureUTF8( psz );
+}
+
+static inline int isurlsafe( int c )
+{
+ return ( (unsigned char)( c - 'a' ) < 26 )
+ || ( (unsigned char)( c - 'A' ) < 26 )
+ || ( (unsigned char)( c - '0' ) < 10 )
+ /* Hmm, we should not encode character that are allowed in URLs
+ * (even if they are not URL-safe), nor URL-safe characters.
+ * We still encode some of them because of Microsoft's crap browser.
+ */
+ || ( strchr( "-_.", c ) != NULL );
+}
+
+static inline char url_hexchar( int c )
+{
+ return ( c < 10 ) ? c + '0' : c + 'A' - 10;
+}
+
+/**
+ * encode_URI_component
+ * Encodes an URI component.
+ *
+ * @param psz_url 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)
+ */
+char *encode_URI_component( const char *psz_url )
+{
+ char psz_enc[3 * strlen( psz_url ) + 1], *out = psz_enc;
+ const uint8_t *in;
+
+ for( in = (const uint8_t *)psz_url; *in; in++ )
+ {
+ uint8_t c = *in;
+
+ if( isurlsafe( c ) )
+ *out++ = (char)c;
+ else
+ if ( c == ' ')
+ *out++ = '+';
+ else
+ {
+ *out++ = '%';
+ *out++ = url_hexchar( c >> 4 );
+ *out++ = url_hexchar( c & 0xf );
+ }
+ }
+ *out++ = '\0';
+
+ return strdup( psz_enc );
}
/**
return psz_temp;
}
+
+/****************************************************************************
+ * String formating functions
+ ****************************************************************************/
+char *str_format_time(char *tformat )
+{
+ char buffer[255];
+ time_t curtime;
+#if defined(HAVE_LOCALTIME_R)
+ struct tm loctime;
+#else
+ struct tm *loctime;
+#endif
+
+ /* Get the current time. */
+ curtime = time( NULL );
+
+ /* Convert it to local time representation. */
+#if defined(HAVE_LOCALTIME_R)
+ localtime_r( &curtime, &loctime );
+ strftime( buffer, 255, tformat, &loctime );
+#else
+ loctime = localtime( &curtime );
+ strftime( buffer, 255, tformat, loctime );
+#endif
+ return strdup( buffer );
+}
+
+#define INSERT_STRING( check, string ) \
+ if( check && string ) \
+ { \
+ int len = strlen( string ); \
+ dst = realloc( dst, \
+ i_size = i_size + len + 1 ); \
+ strncpy( d, string, len+1 ); \
+ d += len; \
+ } \
+ else \
+ { \
+ *d = '-'; \
+ d++; \
+ }
+char *__str_format_meta( vlc_object_t *p_object, char *string )
+{
+ char *s = string;
+ char *dst = malloc( 1000 );
+ char *d = dst;
+ int b_is_format = 0;
+ char buf[10];
+ int i_size = strlen( string );
+
+ playlist_t *p_playlist = pl_Yield( p_object );
+ input_thread_t *p_input = p_playlist->p_input;
+ input_item_t *p_item = NULL;
+ pl_Release( p_object );
+ if( p_input )
+ {
+ vlc_object_yield( p_input );
+ p_item = p_input->input.p_item;
+ if( p_item )
+ vlc_mutex_lock( &p_item->lock );
+ }
+
+ sprintf( dst, string );
+
+ while( *s )
+ {
+ if( b_is_format )
+ {
+ switch( *s )
+ {
+ case 'a':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_artist );
+ break;
+ case 'b':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_album );
+ break;
+ case 'c':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_copyright );
+ break;
+ case 'd':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_description );
+ break;
+ case 'e':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_encodedby );
+ break;
+ case 'g':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_genre );
+ break;
+ case 'l':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_language );
+ break;
+ case 'n':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_tracknum );
+ break;
+ case 'p':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_nowplaying );
+ break;
+ case 'r':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_rating );
+ break;
+ case 's':
+ {
+ char *lang;
+ if( p_input )
+ {
+ lang = var_GetString( p_input, "sub-language" );
+ }
+ else
+ {
+ lang = strdup( "-" );
+ }
+ INSERT_STRING( 1, lang );
+ free( lang );
+ break;
+ }
+ case 't':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_title );
+ break;
+ case 'u':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_url );
+ break;
+ case 'A':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_date );
+ break;
+ case 'B':
+ if( p_input )
+ {
+ snprintf( buf, 10, "%d",
+ var_GetInteger( p_input, "bit-rate" )/1000 );
+ }
+ else
+ {
+ sprintf( buf, "-" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'C':
+ if( p_input )
+ {
+ snprintf( buf, 10, "%d",
+ var_GetInteger( p_input, "chapter" ) );
+ }
+ else
+ {
+ sprintf( buf, "-" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'D':
+ if( p_item )
+ {
+ sprintf( buf, "%02d:%02d:%02d",
+ (int)(p_item->i_duration/(3600000000)),
+ (int)((p_item->i_duration/(60000000))%60),
+ (int)((p_item->i_duration/1000000)%60) );
+ }
+ else
+ {
+ sprintf( buf, "--:--:--" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'F':
+ INSERT_STRING( p_item, p_item->psz_uri );
+ break;
+ case 'I':
+ if( p_input )
+ {
+ snprintf( buf, 10, "%d",
+ var_GetInteger( p_input, "title" ) );
+ }
+ else
+ {
+ sprintf( buf, "-" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'L':
+ if( p_item && p_input )
+ {
+ sprintf( buf, "%02d:%02d:%02d",
+ (int)((p_item->i_duration-p_input->i_time)/(3600000000)),
+ (int)(((p_item->i_duration-p_input->i_time)/(60000000))%60),
+ (int)(((p_item->i_duration-p_input->i_time)/1000000)%60) );
+ }
+ else
+ {
+ sprintf( buf, "--:--:--" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'N':
+ INSERT_STRING( p_item, p_item->psz_name );
+ break;
+ case 'O':
+ {
+ char *lang;
+ if( p_input )
+ {
+ lang = var_GetString( p_input, "audio-language" );
+ }
+ else
+ {
+ lang = strdup( "-" );
+ }
+ INSERT_STRING( 1, lang );
+ free( lang );
+ break;
+ }
+ case 'P':
+ if( p_input )
+ {
+ snprintf( buf, 10, "%2.1lf",
+ var_GetFloat( p_input, "position" ) * 100. );
+ }
+ else
+ {
+ sprintf( buf, "--.-%%" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'R':
+ if( p_input )
+ {
+ int r = var_GetInteger( p_input, "rate" );
+ snprintf( buf, 10, "%d.%d", r/1000, r%1000 );
+ }
+ else
+ {
+ sprintf( buf, "-" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'S':
+ if( p_input )
+ {
+ int r = var_GetInteger( p_input, "sample-rate" );
+ snprintf( buf, 10, "%d.%d", r/1000, (r/100)%10 );
+ }
+ else
+ {
+ sprintf( buf, "-" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'T':
+ if( p_input )
+ {
+ sprintf( buf, "%02d:%02d:%02d",
+ (int)(p_input->i_time/(3600000000)),
+ (int)((p_input->i_time/(60000000))%60),
+ (int)((p_input->i_time/1000000)%60) );
+ }
+ else
+ {
+ sprintf( buf, "--:--:--" );
+ }
+ INSERT_STRING( 1, buf );
+ break;
+ case 'U':
+ INSERT_STRING( p_item && p_item->p_meta,
+ p_item->p_meta->psz_publisher );
+ break;
+ case 'V':
+ {
+ audio_volume_t volume;
+ aout_VolumeGet( p_object, &volume );
+ snprintf( buf, 10, "%d", volume );
+ INSERT_STRING( 1, buf );
+ break;
+ }
+ case '_':
+ *d = '\n';
+ d++;
+ break;
+
+ default:
+ *d = *s;
+ d++;
+ break;
+ }
+ b_is_format = 0;
+ }
+ else if( *s == '$' )
+ {
+ b_is_format = 1;
+ }
+ else
+ {
+ *d = *s;
+ d++;
+ }
+ s++;
+ }
+ *d = '\0';
+
+ if( p_input )
+ {
+ vlc_object_release( p_input );
+ if( p_item )
+ vlc_mutex_unlock( &p_item->lock );
+ }
+
+ return dst;
+}