diff --git a/src/libvlccore.sym b/src/libvlccore.sym index ac68b0e..d72c10f 100644 --- a/src/libvlccore.sym +++ b/src/libvlccore.sym @@ -216,6 +216,7 @@ libvlc_InternalInit libvlc_InternalWait libvlc_Quit LocaleFree +make_URI mdate module_config_free module_config_get diff --git a/include/vlc/libvlc.h b/include/vlc/libvlc.h index 4085e25..5794c0a 100644 --- a/include/vlc/libvlc.h +++ b/include/vlc/libvlc.h @@ -1065,7 +1065,7 @@ VLC_PUBLIC_API void libvlc_video_set_track( libvlc_media_player_t *, int, libvlc * \param i_height the snapshot's height * \param p_e an initialized exception pointer */ -VLC_PUBLIC_API void libvlc_video_take_snapshot( libvlc_media_player_t *, char *,unsigned int, unsigned int, libvlc_exception_t * ); +VLC_PUBLIC_API void libvlc_video_take_snapshot( libvlc_media_player_t *, const char *,unsigned int, unsigned int, libvlc_exception_t * ); /** * Resize the current video output window. diff --git a/include/vlc_url.h b/include/vlc_url.h index dc2a021..448c685 100644 --- a/include/vlc_url.h +++ b/include/vlc_url.h @@ -48,8 +48,9 @@ struct vlc_url_t VLC_EXPORT( char *, unescape_URI_duplicate, ( const char *psz ) ); VLC_EXPORT( void, unescape_URI, ( char *psz ) ); VLC_EXPORT( char *, decode_URI_duplicate, ( const char *psz ) ); -VLC_EXPORT( void, decode_URI, ( char *psz ) ); +VLC_EXPORT( char *, decode_URI, ( char *psz ) ); VLC_EXPORT( char *, encode_URI_component, ( const char *psz ) ); +VLC_EXPORT( char *, make_URI, ( const char *path ) ); /***************************************************************************** * vlc_UrlParse: diff --git a/modules/audio_output/pulse.c b/modules/audio_output/pulse.c index 19e4b75..86bd6dd 100644 --- a/modules/audio_output/pulse.c +++ b/modules/audio_output/pulse.c @@ -176,16 +176,15 @@ static int Open ( vlc_object_t *p_this ) goto fail; } - a.maxlength = pa_bytes_per_second(&ss)/4/pa_frame_size(&ss); - a.tlength = a.maxlength*9/10; - a.prebuf = a.tlength/2; - a.minreq = a.tlength/10; - - a.maxlength *= pa_frame_size(&ss); - a.tlength *= pa_frame_size(&ss); - a.prebuf *= pa_frame_size(&ss); - a.minreq *= pa_frame_size(&ss); - + /* Reduce overall latency to 200mS to reduce audible clicks + * Also pulse minreq and internal buffers are now 20mS which reduces resampling + */ + a.tlength = pa_bytes_per_second(&ss)/5; + a.maxlength = a.tlength * 2; + a.prebuf = a.tlength; + a.minreq = a.tlength / 10; + + /* Buffer size is 20mS */ p_sys->buffer_size = a.minreq; /* Initialise the speaker map setup above */ @@ -240,7 +239,7 @@ static int Open ( vlc_object_t *p_this ) pa_stream_set_write_callback(p_sys->stream, stream_request_cb, p_aout); pa_stream_set_latency_update_callback(p_sys->stream, stream_latency_update_cb, p_aout); - if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE, NULL, NULL) < 0) { + if (pa_stream_connect_playback(p_sys->stream, NULL, &a, PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE|PA_STREAM_ADJUST_LATENCY, NULL, NULL) < 0) { msg_Err(p_aout, "Failed to connect stream: %s", pa_strerror(pa_context_errno(p_sys->context))); goto unlock_and_fail; } @@ -448,16 +447,11 @@ static void stream_request_cb(pa_stream *s, size_t length, void *userdata) { latency = 0; } + PULSE_DEBUG( "Pulse stream request latency=%"PRId64"", latency); next_date = mdate() + latency; - if(p_sys->start_date < next_date + AOUT_PTS_TOLERANCE ){ - /* - vlc_mutex_lock( &p_aout->output_fifo_lock ); - p_buffer = aout_FifoPop( p_aout, &p_aout->output.fifo ); - vlc_mutex_unlock( &p_aout->output_fifo_lock ); - */ p_buffer = aout_OutputNextBuffer( p_aout, next_date, 0); } } diff --git a/modules/control/http/http.c b/modules/control/http/http.c index 5bce03b..75f24c2 100644 --- a/modules/control/http/http.c +++ b/modules/control/http/http.c @@ -555,18 +555,11 @@ int HandlerCallback( httpd_handler_sys_t *p_args, int i_env = 0; char **ppsz_env = NULL; char *psz_tmp; - char sep; size_t i_buffer; char *p_buffer; char *psz_cwd, *psz_file = NULL; int i_ret; -#ifdef WIN32 - sep = '\\'; -#else - sep = '/'; -#endif - /* Create environment for the CGI */ TAB_APPEND( i_env, ppsz_env, strdup("GATEWAY_INTERFACE=CGI/1.1") ); TAB_APPEND( i_env, ppsz_env, strdup("SERVER_PROTOCOL=HTTP/1.1") ); @@ -677,7 +670,7 @@ int HandlerCallback( httpd_handler_sys_t *p_args, } } - psz_file = strrchr( p_args->file.file, sep ); + psz_file = strrchr( p_args->file.file, DIR_SEP_CHAR ); if( psz_file != NULL ) { psz_file++; @@ -694,7 +687,7 @@ int HandlerCallback( httpd_handler_sys_t *p_args, NULL ); psz_tmp = strdup( p_args->file.file ); - p = strrchr( psz_tmp, sep ); + p = strrchr( psz_tmp, DIR_SEP_CHAR ); if( p != NULL ) { *p = '\0'; diff --git a/modules/control/http/util.c b/modules/control/http/util.c index a8b7861..69de4a6 100644 --- a/modules/control/http/util.c +++ b/modules/control/http/util.c @@ -120,14 +120,6 @@ int ParseDirectory( intf_thread_t *p_intf, char *psz_root, int i_dirlen; - char sep; - -#if defined( WIN32 ) - sep = '\\'; -#else - sep = '/'; -#endif - if( ( p_dir = utf8_opendir( psz_dir ) ) == NULL ) { if( errno != ENOENT && errno != ENOTDIR ) @@ -145,7 +137,7 @@ int ParseDirectory( intf_thread_t *p_intf, char *psz_root, msg_Dbg( p_intf, "dir=%s", psz_dir ); - snprintf( dir, sizeof( dir ), "%s%c.access", psz_dir, sep ); + snprintf( dir, sizeof( dir ), "%s"DIR_SEP".access", psz_dir ); if( ( file = utf8_fopen( dir, "r" ) ) != NULL ) { char line[1024]; @@ -179,7 +171,7 @@ int ParseDirectory( intf_thread_t *p_intf, char *psz_root, fclose( file ); } - snprintf( dir, sizeof( dir ), "%s%c.hosts", psz_dir, sep ); + snprintf( dir, sizeof( dir ), "%s"DIR_SEP".hosts", psz_dir ); p_acl = ACL_Create( p_intf, false ); if( ACL_LoadFile( p_acl, dir ) ) { @@ -210,7 +202,7 @@ int ParseDirectory( intf_thread_t *p_intf, char *psz_root, continue; } - snprintf( dir, sizeof( dir ), "%s%c%s", psz_dir, sep, psz_filename ); + snprintf( dir, sizeof( dir ), "%s"DIR_SEP"%s", psz_dir, psz_filename ); free( psz_filename ); if( ParseDirectory( p_intf, psz_root, dir ) ) @@ -912,13 +904,12 @@ char *RealPath( const char *psz_src ) char *psz_dir; char *p; int i_len = strlen(psz_src); - const char sep = DIR_SEP_CHAR; psz_dir = malloc( i_len + 2 ); strcpy( psz_dir, psz_src ); /* Add a trailing sep to ease the .. step */ - psz_dir[i_len] = sep; + psz_dir[i_len] = DIR_SEP_CHAR; psz_dir[i_len + 1] = '\0'; #if (DIR_SEP_CHAR != '/') @@ -926,18 +917,18 @@ char *RealPath( const char *psz_src ) p = psz_dir; while( (p = strchr( p, '/' )) != NULL ) { - *p = sep; + *p = DIR_SEP_CHAR; } #endif /* FIXME: this could be O(N) rather than O(N²)... */ /* Remove multiple separators and /./ */ p = psz_dir; - while( (p = strchr( p, sep )) != NULL ) + while( (p = strchr( p, DIR_SEP_CHAR )) != NULL ) { - if( p[1] == sep ) + if( p[1] == DIR_SEP_CHAR ) memmove( &p[1], &p[2], strlen(&p[2]) + 1 ); - else if( p[1] == '.' && p[2] == sep ) + else if( p[1] == '.' && p[2] == DIR_SEP_CHAR ) memmove( &p[1], &p[3], strlen(&p[3]) + 1 ); else p++; @@ -955,13 +946,13 @@ char *RealPath( const char *psz_src ) { /* Fix all .. dir */ p = psz_dir + 3; - while( (p = strchr( p, sep )) != NULL ) + while( (p = strchr( p, DIR_SEP_CHAR )) != NULL ) { - if( p[-1] == '.' && p[-2] == '.' && p[-3] == sep ) + if( p[-1] == '.' && p[-2] == '.' && p[-3] == DIR_SEP_CHAR ) { char *q; p[-3] = '\0'; - if( (q = strrchr( psz_dir, sep )) != NULL ) + if( (q = strrchr( psz_dir, DIR_SEP_CHAR )) != NULL ) { memmove( q + 1, p + 1, strlen(p + 1) + 1 ); p = q + 1; @@ -979,8 +970,8 @@ char *RealPath( const char *psz_src ) /* Remove trailing sep if there are at least 2 sep in the string * (handles the C:\ stuff) */ - p = strrchr( psz_dir, sep ); - if( p != NULL && p[1] == '\0' && p != strchr( psz_dir, sep ) ) + p = strrchr( psz_dir, DIR_SEP_CHAR ); + if( p != NULL && p[1] == '\0' && p != strchr( psz_dir, DIR_SEP_CHAR ) ) *p = '\0'; return psz_dir; diff --git a/modules/gui/qt4/main_interface.cpp b/modules/gui/qt4/main_interface.cpp index ab0eb9a..8baf686 100644 --- a/modules/gui/qt4/main_interface.cpp +++ b/modules/gui/qt4/main_interface.cpp @@ -637,7 +637,7 @@ QSize MainInterface::sizeHint() const */ void MainInterface::doComponentsUpdate() { - if( isFullScreen() ) return; + if( isFullScreen() || isMaximized() ) return; msg_Dbg( p_intf, "Updating the geometry" ); /* Here we resize to sizeHint() and not adjustsize because we want diff --git a/modules/misc/playlist/xspf.c b/modules/misc/playlist/xspf.c index 9b7ef96..0f5f82c 100644 --- a/modules/misc/playlist/xspf.c +++ b/modules/misc/playlist/xspf.c @@ -34,11 +34,14 @@ #include #include #include -#include +#include #include "xspf.h" #include +static void xspf_export_item( playlist_item_t *, FILE *, int * ); +static void xspf_extension_item( playlist_item_t *, FILE *, int * ); + /** * \brief Prints the XSPF header to file, writes each item by xspf_export_item() * and closes the open xml elements @@ -139,7 +142,7 @@ static void xspf_export_item( playlist_item_t *p_item, FILE *p_file, if( psz_uri && *psz_uri ) { - psz = assertUTF8URI( psz_uri ); + psz = make_URI( psz_uri ); fprintf( p_file, "\t\t\t%s\n", psz ); free( psz ); } @@ -209,7 +212,7 @@ static void xspf_export_item( playlist_item_t *p_item, FILE *p_file, if( psz == NULL ) psz = strdup( "" ); if( !EMPTY_STR( psz ) ) { - psz_uri = assertUTF8URI( psz ); + psz_uri = make_URI( psz ); fprintf( p_file, "\t\t\t%s\n", psz_uri ); free( psz_uri ); } @@ -284,89 +287,3 @@ static void xspf_extension_item( playlist_item_t *p_item, FILE *p_file, return; } - -/** - * \param psz_name the location of the media ressource (e.g. local file, - * device, network stream, etc.) - * \return a new char buffer which asserts that the location is valid UTF-8 - * and a valid URI - * \note the returned buffer must be freed, when it isn't used anymore - */ -static char *assertUTF8URI( char *psz_name ) -{ - char *psz_ret = NULL; /**< the new result buffer to return */ - char *psz_s = NULL, *psz_d = NULL; /**< src & dest pointers for URI conversion */ - bool b_uri_is_file = false; /**< we do additional %-encoding if the URI is a file:// one */ - - if( !psz_name || !*psz_name ) - return NULL; - - /* check that string is valid UTF-8 */ - /* XXX: Why do we even need to do that ? (all strings in core are UTF-8 encoded */ - if( !( psz_s = EnsureUTF8( psz_name ) ) ) - return NULL; - - /* max. 3x for URI conversion (percent escaping) and - 8 bytes for "file://" and NULL-termination */ - psz_ret = (char *)malloc( strlen(psz_name)*6*3+8 ); - if( !psz_ret ) - return NULL; - - /** \todo check for a valid scheme part preceding the colon */ - if( strstr( psz_s, "://") != NULL ) - { - size_t i_delim = strcspn( psz_s, ":" ); - i_delim++; /* skip the ':' */ - strncpy( psz_ret, psz_s, i_delim ); - psz_d = psz_ret + i_delim; - - if( !strncmp( psz_s, "file://", 7 ) ) - b_uri_is_file = true; - - psz_s += i_delim; - } - /* assume "file" scheme if no scheme-part is included */ - else - { - strcpy( psz_ret, "file://" ); - psz_d = psz_ret + 7; - b_uri_is_file = true; - } - - while( *psz_s ) - { - /* percent-encode all non-ASCII and the XML special characters and the percent sign itself */ - if( *psz_s & B10000000 || - *psz_s == '<' || - *psz_s == '>' || - *psz_s == '&' || - *psz_s == ' ' || - *psz_s == '+' || - *psz_s == '%' || - *psz_s == '\\' || - ( b_uri_is_file && ( - *psz_s == ':' || - *psz_s == '"' || - *psz_s == '?' || - *psz_s == '#' || - *psz_s == '[' || - *psz_s == ']' || - *psz_s == '@' ) - ) - ) - { - *psz_d++ = '%'; - *psz_d++ = hexchars[(*psz_s >> 4) & B00001111]; - *psz_d++ = hexchars[*psz_s & B00001111]; - } - else - { - *psz_d++ = *psz_s; - } - - psz_s++; - } - *psz_d = '\0'; - - return (char *)realloc( psz_ret, strlen( psz_ret ) + 1 ); -} diff --git a/modules/misc/playlist/xspf.h b/modules/misc/playlist/xspf.h index 9a8ac6b..adfaa90 100644 --- a/modules/misc/playlist/xspf.h +++ b/modules/misc/playlist/xspf.h @@ -33,6 +33,3 @@ const char hexchars[16] = "0123456789ABCDEF"; /* prototypes */ int xspf_export_playlist( vlc_object_t * ); -static void xspf_export_item( playlist_item_t *, FILE *, int * ); -static void xspf_extension_item( playlist_item_t *, FILE *, int * ); -static char *assertUTF8URI( char * ); diff --git a/modules/misc/screensaver.c b/modules/misc/screensaver.c index 24448bc..b5d8946 100644 --- a/modules/misc/screensaver.c +++ b/modules/misc/screensaver.c @@ -50,6 +50,10 @@ #define GS_PATH "/org/gnome/ScreenSaver" #define GS_INTERFACE "org.gnome.ScreenSaver" +#define FDS_SERVICE "org.freedesktop.ScreenSaver" +#define FDS_PATH "/ScreenSaver" +#define FDS_INTERFACE "org.freedesktop.ScreenSaver" + #endif /***************************************************************************** @@ -67,8 +71,11 @@ static void poke_screensaver( intf_thread_t *p_intf, DBusConnection *p_connection ); static void screensaver_send_message_void ( intf_thread_t *p_intf, DBusConnection *p_connection, + const char *psz_service, + const char *psz_path, + const char *psz_interface, const char *psz_name ); -static bool screensaver_is_running( DBusConnection *p_connection ); +static bool screensaver_is_running( DBusConnection *p_connection, const char *psz_service ); struct intf_sys_t @@ -235,34 +242,47 @@ static DBusConnection * dbus_init( intf_thread_t *p_intf ) static void poke_screensaver( intf_thread_t *p_intf, DBusConnection *p_connection ) { - if( screensaver_is_running( p_connection ) ) + if( screensaver_is_running( p_connection, GS_SERVICE ) ) { # ifdef SCREENSAVER_DEBUG msg_Dbg( p_intf, "found a running gnome-screensaver instance" ); # endif /* gnome-screensaver changed it's D-Bus interface, so we need both */ - screensaver_send_message_void( p_intf, p_connection, "Poke" ); - screensaver_send_message_void( p_intf, p_connection, - "SimulateUserActivity" ); + screensaver_send_message_void( p_intf, p_connection, GS_SERVICE, GS_PATH, + GS_INTERFACE, "Poke" ); + screensaver_send_message_void( p_intf, p_connection, GS_SERVICE, GS_PATH, + GS_INTERFACE, "SimulateUserActivity" ); + } + else if( screensaver_is_running( p_connection, FDS_SERVICE ) ) + { +# ifdef SCREENSAVER_DEBUG + msg_Dbg( p_intf, "found a running freedesktop-screensaver instance" ); +# endif + screensaver_send_message_void( p_intf, p_connection, FDS_SERVICE, FDS_PATH, + FDS_INTERFACE, "SimulateUserActivity" ); } # ifdef SCREENSAVER_DEBUG else { - msg_Dbg( p_intf, "found no running gnome-screensaver instance" ); + msg_Dbg( p_intf, "found no running (gnome|freedesktop)-screensaver instance" ); } # endif + } static void screensaver_send_message_void ( intf_thread_t *p_intf, DBusConnection *p_connection, + const char *psz_service, + const char *psz_path, + const char *psz_interface, const char *psz_name ) { DBusMessage *p_message; if( !p_connection || !psz_name ) return; - p_message = dbus_message_new_method_call( GS_SERVICE, GS_PATH, - GS_INTERFACE, psz_name ); + p_message = dbus_message_new_method_call( psz_service, psz_path, + psz_interface, psz_name ); if( p_message == NULL ) { msg_Err( p_intf, "DBUS initialization failed: message initialization" ); @@ -279,7 +299,7 @@ static void screensaver_send_message_void ( intf_thread_t *p_intf, dbus_message_unref( p_message ); } -static bool screensaver_is_running( DBusConnection *p_connection ) +static bool screensaver_is_running( DBusConnection *p_connection, const char *psz_service ) { DBusError error; bool b_return; @@ -287,7 +307,7 @@ static bool screensaver_is_running( DBusConnection *p_connection ) if( !p_connection ) return false; dbus_error_init( &error ); - b_return = dbus_bus_name_has_owner( p_connection, GS_SERVICE, &error ); + b_return = dbus_bus_name_has_owner( p_connection, psz_service, &error ); if( dbus_error_is_set( &error ) ) dbus_error_free (&error); return b_return; diff --git a/src/control/video.c b/src/control/video.c index 567532c..81c0052 100644 --- a/src/control/video.c +++ b/src/control/video.c @@ -104,7 +104,7 @@ void libvlc_toggle_fullscreen( libvlc_media_player_t *p_mi, } void -libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, char *psz_filepath, +libvlc_video_take_snapshot( libvlc_media_player_t *p_mi, const char *psz_filepath, unsigned int i_width, unsigned int i_height, libvlc_exception_t *p_e ) { vout_thread_t *p_vout = GetVout( p_mi, p_e ); diff --git a/src/test/url.c b/src/test/url.c index a097aca..d03e48c 100644 --- a/src/test/url.c +++ b/src/test/url.c @@ -29,6 +29,7 @@ #include #include +#include typedef char * (*conv_t) (const char *); @@ -60,8 +61,15 @@ static inline void test_b64 (const char *in, const char *out) test (vlc_b64_encode, in, out); } +static inline void test_path (const char *in, const char *out) +{ + test (make_URI, in, out); +} + int main (void) { + int val; + (void)setvbuf (stdout, NULL, _IONBF, 0); test_decode ("this_should_not_be_modified_1234", "this_should_not_be_modified_1234"); @@ -93,5 +101,24 @@ int main (void) test_b64 ("fooba", "Zm9vYmE="); test_b64 ("foobar", "Zm9vYmFy"); + /* Path test */ + test_path ("file:///", "file:///"); + test_path ("http://www.example.com/%7Ejohn/", + "http://www.example.com/%7Ejohn/"); + test_path ("/", "file:///"); + test_path ("/home/john/", "file:///home/john/"); + test_path ("/home/john/music.ogg", "file:///home/john/music.ogg"); + //test_path ("\\\\server/pub/music.ogg", "file://server/pub/music.ogg"); + + /*int fd = open (".", O_RDONLY); + assert (fd != -1);*/ + val = chdir ("/tmp"); + assert (val != -1); + test_path ("movie.ogg", "file:///tmp/movie.ogg"); + test_path (".", "file:///tmp/."); + test_path ("", "file:///tmp/"); + /*val = fchdir (fd); + assert (val != -1);*/ + return 0; } diff --git a/src/text/strings.c b/src/text/strings.c index 8a0229c..860aae7 100644 --- a/src/text/strings.c +++ b/src/text/strings.c @@ -130,7 +131,7 @@ void unescape_URI( char *psz ) } /** - * Decode encoded URI string + * Decode encoded URI component. See also decode_URI(). * \return decoded duplicated string */ char *decode_URI_duplicate( const char *psz ) @@ -141,14 +142,23 @@ char *decode_URI_duplicate( const char *psz ) } /** - * Decode encoded URI string in place - * \return nothing + * 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 */ -void decode_URI( char *psz ) +char *decode_URI( char *psz ) { unsigned char *in = (unsigned char *)psz, *out = in, c; + if( psz == NULL ) - return; + return NULL; while( ( c = *in++ ) != '\0' ) { @@ -160,14 +170,14 @@ void decode_URI( char *psz ) if( ( ( hex[0] = *in++ ) == 0 ) || ( ( hex[1] = *in++ ) == 0 ) ) - return; + return NULL; hex[2] = '\0'; *out++ = (unsigned char)strtoul( hex, NULL, 0x10 ); break; } - case '+': + case '+': /* This is HTTP forms, not URI decoding... */ *out++ = ' '; break; @@ -182,6 +192,7 @@ void decode_URI( char *psz ) } *out = '\0'; EnsureUTF8( psz ); + return psz; } static inline bool isurisafe( int c ) @@ -193,23 +204,13 @@ static inline bool isurisafe( int c ) || ( strchr( "-._~", c ) != NULL ); } -/** - * 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 ) +static char *encode_URI_bytes (const char *psz_uri, size_t len) { - char *psz_enc = malloc ((3 * strlen (psz_uri)) + 1), *out = psz_enc; - + char *psz_enc = malloc (3 * len + 1), *out = psz_enc; if (psz_enc == NULL) return NULL; - while (*psz_uri) + for (size_t i = 0; i < len; i++) { static const char hex[16] = "0123456789ABCDEF"; uint8_t c = *psz_uri; @@ -232,6 +233,21 @@ char *encode_URI_component( const char *psz_uri ) 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)); +} + + static const struct xml_entity_s { char psz_entity[8]; @@ -1120,3 +1136,78 @@ void path_sanitize( char *str ) str++; } } + +#include + +/** + * Convert a file path to an URI. If already an URI, do nothing. + */ +char *make_URI (const char *path) +{ + if (path == NULL) + return NULL; + 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] == ':')) + { + if (asprintf (&buf, "file:///%c:", path[0]) == -1) + buf = NULL; + path += 2; + } + else +#endif +#if 0 + /* Windows UNC paths (file://host/share/path instead of file:///path) */ + if (!strncmp (path, "\\\\", 2)) + { + path += 2; + buf = strdup ("file://"); + } + else +#endif + 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; + } +}