X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=src%2Fconfig%2Ffile.c;h=6c8340ee8939a34a1c8427b9752275185d75fd64;hb=4ab00885f95809f3065340a69d3f87fe4d9b56b0;hp=137e1251b0a7abb8ebdeebeddf80f9c4c2fd6bf4;hpb=ed5205fe93f276d92c8571c43551d7e0a8c05bab;p=vlc diff --git a/src/config/file.c b/src/config/file.c index 137e1251b0..6c8340ee89 100644 --- a/src/config/file.c +++ b/src/config/file.c @@ -16,24 +16,32 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU 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 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. *****************************************************************************/ -#include -#include "../libvlc.h" -#include "vlc_charset.h" -#include "vlc_keys.h" +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif #include /* errno */ -#include - -#ifdef HAVE_LIMITS_H -# include +#include +#include +#include +#include +#ifdef __APPLE__ +# include +#else +#include #endif -#include "config.h" +#include +#include "../libvlc.h" +#include "vlc_charset.h" +#include "vlc_keys.h" + +#include "configuration.h" #include "modules/modules.h" static char *ConfigKeyToString( int ); @@ -43,25 +51,32 @@ static inline char *strdupnull (const char *src) return src ? strdup (src) : NULL; } -static inline char *_strdupnull (const char *src) +/** + * Get the user's configuration file + */ +static char *config_GetConfigFile( vlc_object_t *obj ) { - return src ? strdup (_(src)) : NULL; -} + char *psz_file = config_GetPsz( obj, "config" ); + if( psz_file == NULL ) + { + char *psz_dir = config_GetUserDir( VLC_CONFIG_DIR ); + if( asprintf( &psz_file, "%s" DIR_SEP CONFIG_FILE, psz_dir ) == -1 ) + psz_file = NULL; + free( psz_dir ); + } + return psz_file; +} -static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode ) +static FILE *config_OpenConfigFile( vlc_object_t *p_obj ) { - char *psz_filename = p_obj->p_libvlc->psz_configfile; - FILE *p_stream; - - if( !psz_filename ) - { - psz_filename = config_GetConfigFile( p_obj->p_libvlc ); - } + char *psz_filename = config_GetConfigFile( p_obj ); + if( psz_filename == NULL ) + return NULL; msg_Dbg( p_obj, "opening config file (%s)", psz_filename ); - p_stream = utf8_fopen( psz_filename, mode ); + FILE *p_stream = utf8_fopen( psz_filename, "rt" ); if( p_stream == NULL && errno != ENOENT ) { msg_Err( p_obj, "cannot open config file (%s): %m", @@ -69,15 +84,18 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode ) } #if !( defined(WIN32) || defined(__APPLE__) || defined(SYS_BEOS) ) - else if( p_stream == NULL && errno == ENOENT && mode[0] == 'r' ) + else if( p_stream == NULL && errno == ENOENT ) { /* This is the fallback for pre XDG Base Directory * Specification configs */ + char *home = config_GetUserDir(VLC_HOME_DIR); char *psz_old; - if( asprintf( &psz_old, "%s" DIR_SEP CONFIG_DIR DIR_SEP CONFIG_FILE, - p_obj->p_libvlc->psz_homedir ) != -1 ) + + if( home != NULL + && asprintf( &psz_old, "%s/.vlc/" CONFIG_FILE, + home ) != -1 ) { - p_stream = utf8_fopen( psz_old, mode ); + p_stream = utf8_fopen( psz_old, "rt" ); if( p_stream ) { /* Old config file found. We want to write it at the @@ -85,20 +103,19 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode ) msg_Info( p_obj->p_libvlc, "Found old config file at %s. " "VLC will now use %s.", psz_old, psz_filename ); char *psz_readme; - if( asprintf(&psz_readme,"%s"DIR_SEP CONFIG_DIR DIR_SEP"README", - p_obj->p_libvlc->psz_homedir ) != -1 ) + if( asprintf(&psz_readme,"%s/.vlc/README", + home ) != -1 ) { FILE *p_readme = utf8_fopen( psz_readme, "wt" ); if( p_readme ) { - fputs( "The VLC media player configuration folder has " - "moved to comply with the XDG Base " - "Directory Specification version 0.6. Your " - "configuration has been copied to the new " - "location (", p_readme ); - fputs( p_obj->p_libvlc->psz_configdir, p_readme ); - fputs( "). You can delete this directory and " - "all its contents.", p_readme ); + fprintf( p_readme, "The VLC media player " + "configuration folder has moved to comply\n" + "with the XDG Base Directory Specification " + "version 0.6. Your\nconfiguration has been " + "copied to the new location:\n%s\nYou can " + "delete this directory and all its contents.", + psz_filename); fclose( p_readme ); } free( psz_readme ); @@ -106,13 +123,10 @@ static FILE *config_OpenConfigFile( vlc_object_t *p_obj, const char *mode ) } free( psz_old ); } + free( home ); } #endif - else if( p_stream != NULL ) - { - p_obj->p_libvlc->psz_configfile = psz_filename; - } - + free( psz_filename ); return p_stream; } @@ -144,18 +158,14 @@ static int strtoi (const char *str) *****************************************************************************/ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) { - vlc_list_t *p_list; FILE *file; - file = config_OpenConfigFile (p_this, "rt"); + file = config_OpenConfigFile (p_this); if (file == NULL) return VLC_EGENERIC; - /* Acquire config file lock */ - vlc_mutex_lock( &p_this->p_libvlc->config_lock ); - /* Look for the selected module, if NULL then save everything */ - p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + module_t **list = module_list_get (NULL); /* Look for UTF-8 Byte Order Mark */ char * (*convert) (const char *) = strdupnull; @@ -172,6 +182,10 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) char line[1024], section[1022]; section[0] = '\0'; + /* Ensure consistent number formatting... */ + locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL); + locale_t baseloc = uselocale (loc); + while (fgets (line, 1024, file) != NULL) { /* Ignore comments and empty lines */ @@ -197,9 +211,9 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) if ((psz_module_name == NULL) || (strcmp (psz_module_name, section) == 0)) { - for (int i = 0; i < p_list->i_count; i++) + for (int i = 0; list[i]; i++) { - module_t *m = (module_t *)p_list->p_values[i].p_object; + module_t *m = list[i]; if ((strcmp (section, m->psz_object_name) == 0) && (m->i_config_items > 0)) /* ignore config-less modules */ @@ -246,6 +260,7 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) /* We found it */ errno = 0; + vlc_mutex_lock( p_item->p_lock ); switch( p_item->i_type ) { case CONFIG_ITEM_BOOL: @@ -263,7 +278,7 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) case CONFIG_ITEM_FLOAT: if( !*psz_option_value ) break; /* ignore empty option */ - p_item->value.f = (float)i18n_atof( psz_option_value); + p_item->value.f = (float)atof (psz_option_value); p_item->saved.f = p_item->value.f; break; @@ -275,19 +290,15 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) break; default: - vlc_mutex_lock( p_item->p_lock ); - /* free old string */ free( (char*) p_item->value.psz ); free( (char*) p_item->saved.psz ); p_item->value.psz = convert (psz_option_value); p_item->saved.psz = strdupnull (p_item->value.psz); - - vlc_mutex_unlock( p_item->p_lock ); break; } - + vlc_mutex_unlock( p_item->p_lock ); break; } } @@ -299,9 +310,12 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) } fclose (file); - vlc_list_release( p_list ); - - vlc_mutex_unlock( &p_this->p_libvlc->config_lock ); + module_list_free (list); + if (loc != (locale_t)0) + { + uselocale (baseloc); + freelocale (loc); + } return 0; } @@ -310,7 +324,7 @@ int __config_LoadConfigFile( vlc_object_t *p_this, const char *psz_module_name ) *****************************************************************************/ int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname ) { - if( !psz_dirname && !*psz_dirname ) return -1; + if( !psz_dirname || !*psz_dirname ) return -1; if( utf8_mkdir( psz_dirname, 0700 ) == 0 ) return 0; @@ -344,7 +358,7 @@ int config_CreateDir( vlc_object_t *p_this, const char *psz_dirname ) } static int -config_Write (FILE *file, const char *type, const char *desc, +config_Write (FILE *file, const char *desc, const char *type, bool comment, const char *name, const char *fmt, ...) { va_list ap; @@ -353,7 +367,7 @@ config_Write (FILE *file, const char *type, const char *desc, if (desc == NULL) desc = "?"; - if (fprintf (file, "# %s (%s)\n%s%s=", desc, _(type), + if (fprintf (file, "# %s (%s)\n%s%s=", desc, vlc_gettext (type), comment ? "#" : "", name) < 0) return -1; @@ -369,6 +383,17 @@ config_Write (FILE *file, const char *type, const char *desc, } +static int config_PrepareDir (vlc_object_t *obj) +{ + char *psz_configdir = config_GetUserDir (VLC_CONFIG_DIR); + if (psz_configdir == NULL) + return -1; + + int ret = config_CreateDir (obj, psz_configdir); + free (psz_configdir); + return ret; +} + /***************************************************************************** * config_SaveConfigFile: Save a module's config options. ***************************************************************************** @@ -389,68 +414,59 @@ config_Write (FILE *file, const char *type, const char *desc, * Really stupid no ? *****************************************************************************/ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, - vlc_bool_t b_autosave ) + bool b_autosave ) { module_t *p_parser; - vlc_list_t *p_list; - FILE *file; + FILE *file = NULL; + char *permanent = NULL, *temporary = NULL; char p_line[1024], *p_index2; - int i_sizebuf = 0; - char *p_bigbuffer, *p_index; - vlc_bool_t b_backup; + unsigned long i_sizebuf = 0; + char *p_bigbuffer = NULL, *p_index; + bool b_backup; int i_index; - /* Acquire config file lock */ - vlc_mutex_lock( &p_this->p_libvlc->config_lock ); - - if( p_this->p_libvlc->psz_configfile == NULL ) + if( config_PrepareDir( p_this ) ) { - const char *psz_configdir = p_this->p_libvlc->psz_configdir; - if( !psz_configdir ) /* XXX: This should never happen */ - { - msg_Err( p_this, "no configuration directory defined" ); - vlc_mutex_unlock( &p_this->p_libvlc->config_lock ); - return -1; - } - - config_CreateDir( p_this, psz_configdir ); + msg_Err( p_this, "no configuration directory" ); + goto error; } - file = config_OpenConfigFile( p_this, "rt" ); + file = config_OpenConfigFile( p_this ); if( file != NULL ) { - /* look for file size */ - fseek( file, 0L, SEEK_END ); - i_sizebuf = ftell( file ); - fseek( file, 0L, SEEK_SET ); + struct stat st; + + /* Some users make vlcrc read-only to prevent changes. + * The atomic replacement scheme breaks this "feature", + * so we check for read-only by hand. */ + if (fstat (fileno (file), &st) + || !(st.st_mode & S_IWUSR)) + { + msg_Err (p_this, "configuration file is read-only"); + goto error; + } + i_sizebuf = ( st.st_size < LONG_MAX ) ? st.st_size : 0; } p_bigbuffer = p_index = malloc( i_sizebuf+1 ); if( !p_bigbuffer ) - { - msg_Err( p_this, "out of memory" ); - if( file ) fclose( file ); - vlc_mutex_unlock( &p_this->p_libvlc->config_lock ); - return -1; - } + goto error; p_bigbuffer[0] = 0; /* List all available modules */ - p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); + module_t **list = module_list_get (NULL); /* backup file into memory, we only need to backup the sections we won't * save later on */ - b_backup = 0; + b_backup = false; while( file && fgets( p_line, 1024, file ) ) { if( (p_line[0] == '[') && (p_index2 = strchr(p_line,']'))) { /* we found a section, check if we need to do a backup */ - for( i_index = 0; i_index < p_list->i_count; i_index++ ) + for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ ) { - p_parser = (module_t *)p_list->p_values[i_index].p_object ; - if( ((p_index2 - &p_line[1]) == (int)strlen(p_parser->psz_object_name) ) && !memcmp( &p_line[1], p_parser->psz_object_name, @@ -464,7 +480,7 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, } } - if( i_index == p_list->i_count ) + if( list[i_index] == NULL ) { /* we don't have this section in our list so we need to back * it up */ @@ -475,11 +491,11 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, #endif *p_index2 = ']'; - b_backup = 1; + b_backup = true; } else { - b_backup = 0; + b_backup = false; } } @@ -492,30 +508,59 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, p_index += strlen( p_line ); } } - if( file ) fclose( file ); - + if( file ) + fclose( file ); + file = NULL; /* * Save module config in file */ + permanent = config_GetConfigFile (p_this); + if (!permanent) + { + module_list_free (list); + goto error; + } - file = config_OpenConfigFile (p_this, "wt"); - if( !file ) + if (asprintf (&temporary, "%s.%u", permanent, getpid ()) == -1) { - vlc_list_release( p_list ); - free( p_bigbuffer ); - vlc_mutex_unlock( &p_this->p_libvlc->config_lock ); - return -1; + temporary = NULL; + module_list_free (list); + goto error; + } + + /* The temporary configuration file is per-PID. Therefore SaveConfigFile() + * should be serialized against itself within a given process. */ + static vlc_mutex_t lock = VLC_STATIC_MUTEX; + vlc_mutex_lock (&lock); + + int fd = utf8_open (temporary, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR); + if (fd == -1) + { + vlc_mutex_unlock (&lock); + module_list_free (list); + goto error; + } + file = fdopen (fd, "wt"); + if (file == NULL) + { + close (fd); + vlc_mutex_unlock (&lock); + module_list_free (list); + goto error; } fprintf( file, "\xEF\xBB\xBF###\n### " COPYRIGHT_MESSAGE "\n###\n\n" - "###\n### lines begining with a '#' character are comments\n###\n\n" ); + "###\n### lines beginning with a '#' character are comments\n###\n\n" ); + + /* Ensure consistent number formatting... */ + locale_t loc = newlocale (LC_NUMERIC_MASK, "C", NULL); + locale_t baseloc = uselocale (loc); /* Look for the selected module, if NULL then save everything */ - for( i_index = 0; i_index < p_list->i_count; i_index++ ) + for( i_index = 0; (p_parser = list[i_index]) != NULL; i_index++ ) { module_config_t *p_item, *p_end; - p_parser = (module_t *)p_list->p_values[i_index].p_object ; if( psz_module_name && strcmp( psz_module_name, p_parser->psz_object_name ) ) @@ -538,15 +583,17 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, p_item < p_end; p_item++ ) { - /* Do not save the new value in the configuration file - * if doing an autosave, and the item is not an "autosaved" one. */ - vlc_bool_t b_retain = b_autosave && !p_item->b_autosave; - if ((p_item->i_type & CONFIG_HINT) /* ignore hint */ || p_item->b_removed /* ignore deprecated option */ || p_item->b_unsaveable) /* ignore volatile option */ continue; + vlc_mutex_lock (p_item->p_lock); + + /* Do not save the new value in the configuration file + * if doing an autosave, and the item is not an "autosaved" one. */ + bool b_retain = b_autosave && !p_item->b_autosave; + if (IsConfigIntegerType (p_item->i_type)) { int val = b_retain ? p_item->saved.i : p_item->value.i; @@ -595,27 +642,34 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, : (p_item->orig.psz != NULL); config_Write (file, p_item->psz_text, N_("string"), - modified, p_item->psz_name, "%s", + !modified, p_item->psz_name, "%s", psz_value ? psz_value : ""); - if (b_retain) - break; + if ( !b_retain ) + { - free ((char *)p_item->saved.psz); - if( (psz_value && p_item->orig.psz && - strcmp( psz_value, p_item->orig.psz )) || - !psz_value || !p_item->orig.psz) - p_item->saved.psz = strdupnull (psz_value); - else - p_item->saved.psz = NULL; + free ((char *)p_item->saved.psz); + if( (psz_value && p_item->orig.psz && + strcmp( psz_value, p_item->orig.psz )) || + !psz_value || !p_item->orig.psz) + p_item->saved.psz = strdupnull (psz_value); + else + p_item->saved.psz = NULL; + } } if (!b_retain) - p_item->b_dirty = VLC_FALSE; + p_item->b_dirty = false; + vlc_mutex_unlock (p_item->p_lock); } } - vlc_list_release( p_list ); + module_list_free (list); + if (loc != (locale_t)0) + { + uselocale (baseloc); + freelocale (loc); + } /* * Restore old settings from the config in file @@ -623,95 +677,84 @@ static int SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name, fputs( p_bigbuffer, file ); free( p_bigbuffer ); - fclose( file ); - vlc_mutex_unlock( &p_this->p_libvlc->config_lock ); + /* + * Flush to disk and replace atomically + */ + fflush (file); /* Flush from run-time */ +#ifndef WIN32 + fdatasync (fd); /* Flush from OS */ + /* Atomically replace the file... */ + if (utf8_rename (temporary, permanent)) + utf8_unlink (temporary); + /* (...then synchronize the directory, err, TODO...) */ + /* ...and finally close the file */ + vlc_mutex_unlock (&lock); +#endif + fclose (file); +#ifdef WIN32 + /* Windows cannot remove open files nor overwrite existing ones */ + utf8_unlink (permanent); + if (utf8_rename (temporary, permanent)) + utf8_unlink (temporary); + vlc_mutex_unlock (&lock); +#endif + free (temporary); + free (permanent); return 0; + +error: + if( file ) + fclose( file ); + free (temporary); + free (permanent); + free( p_bigbuffer ); + return -1; } int config_AutoSaveConfigFile( vlc_object_t *p_this ) { - vlc_list_t *p_list; - int i_index, i_count; + size_t i_index; + bool save = false; - if( !p_this ) return -1; + assert( p_this ); /* Check if there's anything to save */ - vlc_mutex_lock( &p_this->p_libvlc->config_lock ); - p_list = vlc_list_find( p_this, VLC_OBJECT_MODULE, FIND_ANYWHERE ); - i_count = p_list->i_count; - for( i_index = 0; i_index < i_count; i_index++ ) + module_t **list = module_list_get (NULL); + for( i_index = 0; list[i_index] && !save; i_index++ ) { - module_t *p_parser = (module_t *)p_list->p_values[i_index].p_object ; + module_t *p_parser = list[i_index]; module_config_t *p_item, *p_end; if( !p_parser->i_config_items ) continue; for( p_item = p_parser->p_config, p_end = p_item + p_parser->confsize; - p_item < p_end; + p_item < p_end && !save; p_item++ ) { - if( p_item->b_autosave && p_item->b_dirty ) break; + vlc_mutex_lock (p_item->p_lock); + save = p_item->b_autosave && p_item->b_dirty; + vlc_mutex_unlock (p_item->p_lock); } - break; } - vlc_list_release( p_list ); - vlc_mutex_unlock( &p_this->p_libvlc->config_lock ); + module_list_free (list); - if( i_index == i_count ) return VLC_SUCCESS; - return SaveConfigFile( p_this, 0, VLC_TRUE ); + return save ? VLC_SUCCESS : SaveConfigFile( p_this, NULL, true ); } int __config_SaveConfigFile( vlc_object_t *p_this, const char *psz_module_name ) { - return SaveConfigFile( p_this, psz_module_name, VLC_FALSE ); -} - -/** - * Get the user's configuration file - */ -char *config_GetConfigFile( libvlc_int_t *p_libvlc ) -{ - char *psz_configfile; - if( asprintf( &psz_configfile, "%s" DIR_SEP CONFIG_FILE, - p_libvlc->psz_configdir ) == -1 ) - return NULL; - return psz_configfile; -} - -/** - * Get the user's configuration file when given with the --config option - */ -char *config_GetCustomConfigFile( libvlc_int_t *p_libvlc ) -{ - char *psz_configfile = config_GetPsz( p_libvlc, "config" ); - if( psz_configfile != NULL ) - { - if( psz_configfile[0] == '~' && psz_configfile[1] == '/' ) - { - /* This is incomplete: we should also support the ~cmassiot/ syntax */ - char *psz_buf; - if( asprintf( &psz_buf, "%s/%s", p_libvlc->psz_homedir, - psz_configfile + 2 ) == -1 ) - { - free( psz_configfile ); - return NULL; - } - free( psz_configfile ); - psz_configfile = psz_buf; - } - } - return psz_configfile; + return SaveConfigFile( p_this, psz_module_name, false ); } int ConfigStringToKey( const char *psz_key ) { int i_key = 0; - unsigned int i; + size_t i; const char *psz_parser = strchr( psz_key, '-' ); while( psz_parser && psz_parser != psz_key ) { - for( i = 0; i < sizeof(vlc_modifiers) / sizeof(key_descriptor_t); i++ ) + for( i = 0; i < vlc_num_modifiers; ++i ) { if( !strncasecmp( vlc_modifiers[i].psz_key_string, psz_key, strlen( vlc_modifiers[i].psz_key_string ) ) ) @@ -722,7 +765,7 @@ int ConfigStringToKey( const char *psz_key ) psz_key = psz_parser + 1; psz_parser = strchr( psz_key, '-' ); } - for( i = 0; i < sizeof(vlc_keys) / sizeof( key_descriptor_t ); i++ ) + for( i = 0; i < vlc_num_keys; ++i ) { if( !strcasecmp( vlc_keys[i].psz_key_string, psz_key ) ) { @@ -735,7 +778,10 @@ int ConfigStringToKey( const char *psz_key ) char *ConfigKeyToString( int i_key ) { - char *psz_key = malloc( 100 ); + // Worst case appears to be 45 characters: + // "Command-Meta-Ctrl-Shift-Alt-Browser Favorites" + enum { keylen=64 }; + char *psz_key = malloc( keylen ); char *p; size_t index; @@ -746,20 +792,20 @@ char *ConfigKeyToString( int i_key ) *psz_key = '\0'; p = psz_key; - for( index = 0; index < (sizeof(vlc_modifiers) / sizeof(key_descriptor_t)); - index++ ) + for( index = 0; index < vlc_num_modifiers; ++index ) { if( i_key & vlc_modifiers[index].i_key_code ) { - p += sprintf( p, "%s-", vlc_modifiers[index].psz_key_string ); + p += snprintf( p, keylen-(psz_key-p), "%s-", + vlc_modifiers[index].psz_key_string ); } } - for( index = 0; index < (sizeof(vlc_keys) / sizeof( key_descriptor_t)); - index++) + for( index = 0; index < vlc_num_keys; ++index ) { if( (int)( i_key & ~KEY_MODIFIER ) == vlc_keys[index].i_key_code ) { - p += sprintf( p, "%s", vlc_keys[index].psz_key_string ); + p += snprintf( p, keylen-(psz_key-p), "%s", + vlc_keys[index].psz_key_string ); break; } }