From 1fd565cba706d6a9e809d14f79ceb92633b62ead Mon Sep 17 00:00:00 2001 From: Vincent Sanders Date: Mon, 28 Apr 2014 16:37:00 +0100 Subject: make GTK configuration handling conform to XDG specification. --- gtk/dialogs/preferences.c | 31 ++++- gtk/gui.c | 342 +++++++++++++++++++++++++++++++++------------- gtk/gui.h | 5 +- gtk/scaffolding.c | 60 +++++--- utils/errors.h | 6 +- utils/filepath.c | 85 +++++++++++- utils/filepath.h | 55 ++++++-- 7 files changed, 447 insertions(+), 137 deletions(-) diff --git a/gtk/dialogs/preferences.c b/gtk/dialogs/preferences.c index a8ff67465..fcf30c8fa 100644 --- a/gtk/dialogs/preferences.c +++ b/gtk/dialogs/preferences.c @@ -19,6 +19,7 @@ #include #include +#include "utils/filepath.h" #include "utils/log.h" #include "utils/utils.h" #include "utils/messages.h" @@ -1001,8 +1002,14 @@ nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid) { + char *choices; + if (resid == GTK_RESPONSE_CLOSE) { - nsoption_write(options_file_location, NULL, NULL); + choices = filepath_append(nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } gtk_widget_hide(GTK_WIDGET(dlg)); } } @@ -1011,18 +1018,32 @@ G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv) { - nsoption_write(options_file_location, NULL, NULL); + char *choices; + + choices = filepath_append(nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } + gtk_widget_hide(GTK_WIDGET(dlg)); - /* delt with it by hiding window, no need to destory widget by - * default */ + /* Delt with it by hiding window, no need to destory widget by + * default. + */ return TRUE; } G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv) { - nsoption_write(options_file_location, NULL, NULL); + char *choices; + + choices = filepath_append(nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } } diff --git a/gtk/gui.c b/gtk/gui.c index fa16900a3..1c108a165 100644 --- a/gtk/gui.c +++ b/gtk/gui.c @@ -79,13 +79,13 @@ #include "utils/utf8.h" #include "utils/utils.h" -char *options_file_location; char *toolbar_indices_file_location; char *res_dir_location; -char *print_options_file_location; char *languages_file_location; char *themelist_file_location; +char *nsgtk_config_home; /* exported global defined in gtk/gui.h */ + GdkPixbuf *favicon_pixbuf; /* favicon default pixbuf */ struct glade_file_location_s *glade_file_location; @@ -243,48 +243,55 @@ nsgtk_init_glade(char **respath) } /** - * Set option defaults for gtk frontend + * Set option defaults for gtk frontend. * * @param defaults The option table to update. * @return error status. */ static nserror set_defaults(struct nsoption_s *defaults) { - char *hdir = getenv("HOME"); - char buf[PATH_MAX]; + char *fname; - /* Set defaults for absent option strings */ - snprintf(buf, PATH_MAX, "%s/.netsurf/Cookies", hdir); - nsoption_setnull_charp(cookie_file, strdup(buf)); - nsoption_setnull_charp(cookie_jar, strdup(buf)); - if (nsoption_charp(cookie_file) == NULL || - nsoption_charp(cookie_jar) == NULL) { - LOG(("Failed initialising cookie options")); - return NSERROR_BAD_PARAMETER; + /* cookie file default */ + fname = filepath_append(nsgtk_config_home, "Cookies"); + if (fname != NULL) { + nsoption_setnull_charp(cookie_file, fname); + } + + /* cookie jar default */ + fname = filepath_append(nsgtk_config_home, "Cookies"); + if (fname != NULL) { + nsoption_setnull_charp(cookie_jar, fname); } - if (nsoption_charp(downloads_directory) == NULL) { - snprintf(buf, PATH_MAX, "%s/", hdir); - nsoption_set_charp(downloads_directory, strdup(buf)); + /* url database default */ + fname = filepath_append(nsgtk_config_home, "URLs"); + if (fname != NULL) { + nsoption_setnull_charp(url_file, fname); } - if (nsoption_charp(url_file) == NULL) { - snprintf(buf, PATH_MAX, "%s/.netsurf/URLs", hdir); - nsoption_set_charp(url_file, strdup(buf)); + /* bookmark database default */ + fname = filepath_append(nsgtk_config_home, "Hotlist"); + if (fname != NULL) { + nsoption_setnull_charp(hotlist_path, fname); } - if (nsoption_charp(hotlist_path) == NULL) { - snprintf(buf, PATH_MAX, "%s/.netsurf/Hotlist", hdir); - nsoption_set_charp(hotlist_path, strdup(buf)); + /* download directory default */ + fname = filepath_append(getenv("HOME"), ""); + if (fname != NULL) { + nsoption_setnull_charp(downloads_directory, fname); } + /* default path to certificates */ nsoption_setnull_charp(ca_path, strdup("/etc/ssl/certs")); - if (nsoption_charp(url_file) == NULL || - nsoption_charp(ca_path) == NULL || - nsoption_charp(downloads_directory) == NULL || - nsoption_charp(hotlist_path) == NULL) { - LOG(("Failed initialising string options")); + if ((nsoption_charp(cookie_file) == NULL) || + (nsoption_charp(cookie_jar) == NULL) || + (nsoption_charp(url_file) == NULL) || + (nsoption_charp(hotlist_path) == NULL) || + (nsoption_charp(downloads_directory) == NULL) || + (nsoption_charp(ca_path) == NULL)) { + LOG(("Failed initialising default resource paths")); return NSERROR_BAD_PARAMETER; } @@ -298,32 +305,6 @@ static nserror set_defaults(struct nsoption_s *defaults) return NSERROR_OK; } -static void check_options(char **respath) -{ - char *hdir = getenv("HOME"); - char buf[PATH_MAX]; - nsoption_set_bool(core_select_menu, true); - - /* Attempt to handle nonsense status bar widths. These may exist - * in people's Choices as the GTK front end used to abuse the - * status bar width option by using it for an absolute value in px. - * The GTK front end now correctly uses it as a proportion of window - * width. Here we assume that a value of less than 15% is wrong - * and set to the default two thirds. */ - if (nsoption_int(toolbar_status_size) < 1500) { - nsoption_set_int(toolbar_status_size, 6667); - } - - /* user options should be stored in the users home directory */ - snprintf(buf, PATH_MAX, "%s/.netsurf/Choices", hdir); - options_file_location = strdup(buf); - - filepath_sfinddef(respath, buf, "Print", "~/.netsurf/"); - LOG(("Using '%s' as Print Settings file", buf)); - print_options_file_location = strdup(buf); - - -} @@ -481,34 +462,6 @@ static void gui_init(int argc, char** argv, char **respath) } -/** - * Check that ~/.netsurf/ exists, and if it doesn't, create it. - */ -static void nsgtk_check_homedir(void) -{ - char *hdir = getenv("HOME"); - char buf[PATH_MAX]; - - if (hdir == NULL) { - /* we really can't continue without a home directory. */ - LOG(("HOME is not set - nowhere to store state!")); - die("NetSurf requires HOME to be set in order to run.\n"); - - } - - snprintf(buf, PATH_MAX, "%s/.netsurf", hdir); - if (access(buf, F_OK) != 0) { - LOG(("You don't have a ~/.netsurf - creating one for you.")); - if (mkdir(buf, S_IRWXU) == -1) { - LOG(("Unable to create %s", buf)); - die("NetSurf requires ~/.netsurf to exist, but it cannot be created.\n"); - } - } else { - if (chmod(buf, S_IRWXU) != 0) { - LOG(("Unable to set permissions on %s", buf)); - } - } -} /** * Ensures output logging stream is correctly configured @@ -601,10 +554,13 @@ static void gui_quit(void) nsgtk_cookies_destroy(); nsgtk_history_destroy(); nsgtk_hotlist_destroy(); - free(print_options_file_location); + free(search_engines_file_location); free(search_default_ico_location); free(toolbar_indices_file_location); + + free(nsgtk_config_home); + gtk_fetch_filetype_fin(); } @@ -990,6 +946,198 @@ uint32_t gtk_gui_gdkkey_to_nskey(GdkEventKey *key) } +/** + * create directory name and check it is acessible and a directory. + */ +static nserror +check_dirname(const char *path, const char *leaf, char **dirname_out) +{ + nserror ret; + char *dirname; + struct stat dirname_stat; + + dirname = filepath_append(path, leaf); + if (dirname == NULL) { + return NSERROR_NOMEM; + } + + /* ensure access is possible and the entry is actualy + * a directory. + */ + if (stat(dirname, &dirname_stat) == 0) { + if (S_ISDIR(dirname_stat.st_mode)) { + if (access(dirname, R_OK | W_OK) == 0) { + *dirname_out = dirname; + return NSERROR_OK; + } else { + ret = NSERROR_PERMISSION; + } + } else { + ret = NSERROR_NOT_DIRECTORY; + } + } else { + ret = NSERROR_NOT_FOUND; + } + + free(dirname); + + return ret;; +} + +/** + * Get the path to the config directory. + * + * @param config_home_out Path to configuration directory. + * @return NSERROR_OK on sucess and \a config_home_out updated else error code. + */ +static nserror get_config_home(char **config_home_out) +{ + nserror ret; + char *home_dir; + char *xdg_config_dir; + char *config_home; + + home_dir = getenv("HOME"); + + /* The old $HOME/.netsurf/ directory should be used if it + * exists and is accessible. + */ + if (home_dir != NULL) { + ret = check_dirname(home_dir, ".netsurf", &config_home); + if (ret == NSERROR_OK) { + LOG(("\"%s\"", config_home)); + *config_home_out = config_home; + return ret; + } + } + + /* $XDG_CONFIG_HOME defines the base directory + * relative to which user specific configuration files + * should be stored. + */ + xdg_config_dir = getenv("XDG_CONFIG_HOME"); + + if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) { + /* If $XDG_CONFIG_HOME is either not set or empty, a + * default equal to $HOME/.config should be used. + */ + + /** @todo the meaning of empty is never defined so I + * am assuming it is a zero length string but is it + * supposed to mean "whitespace" and if so what counts + * as whitespace? (are tabs etc. counted or should + * isspace() be used) + */ + + /* the HOME envvar is required */ + if (home_dir == NULL) { + return NSERROR_NOT_DIRECTORY; + } + + ret = check_dirname(home_dir, ".config/netsurf", &config_home); + if (ret != NSERROR_OK) { + return ret; + } + } else { + ret = check_dirname(xdg_config_dir, "netsurf", &config_home); + if (ret != NSERROR_OK) { + return ret; + } + } + + LOG(("\"%s\"", config_home)); + + *config_home_out = config_home; + return NSERROR_OK; +} + +static nserror create_config_home(char **config_home_out) +{ + char *config_home; + char *home_dir; + char *xdg_config_dir; + nserror ret; + + LOG(("Attempting to create configuration directory")); + + /* $XDG_CONFIG_HOME defines the base directory + * relative to which user specific configuration files + * should be stored. + */ + xdg_config_dir = getenv("XDG_CONFIG_HOME"); + + if ((xdg_config_dir == NULL) || (*xdg_config_dir == 0)) { + home_dir = getenv("HOME"); + + if ((home_dir == NULL) || (*home_dir == 0)) { + return NSERROR_NOT_DIRECTORY; + } + + config_home = filepath_append(home_dir, ".config/netsurf/"); + if (config_home == NULL) { + return NSERROR_NOMEM; + } + } else { + config_home = filepath_append(xdg_config_dir, "netsurf/"); + if (config_home == NULL) { + return NSERROR_NOMEM; + } + } + + /* ensure all elements of path exist (the trailing / is required) */ + ret = filepath_mkdir_all(config_home); + if (ret != NSERROR_OK) { + free(config_home); + return ret; + } + + /* strip the trailing separator */ + config_home[strlen(config_home) - 1] = 0; + + *config_home_out = config_home; + + return NSERROR_OK; +} + +static nserror nsgtk_option_init(int *pargc, char** argv) +{ + nserror ret; + char *choices; + + /* user options setup */ + ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default); + if (ret != NSERROR_OK) { + return ret; + } + + /* Attempt to load the user choices */ + choices = filepath_append(nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_read(choices, nsoptions); + free(choices); + } + + /* overide loaded options with those from commandline */ + nsoption_commandline(pargc, argv, nsoptions); + + /* ensure all options fall within sensible bounds */ + + /* select menus generated by core code */ + nsoption_set_bool(core_select_menu, true); + + /* Attempt to handle nonsense status bar widths. These may exist + * in people's Choices as the GTK front end used to abuse the + * status bar width option by using it for an absolute value in px. + * The GTK front end now correctly uses it as a proportion of window + * width. Here we assume that a value of less than 15% is wrong + * and set to the default two thirds. */ + if (nsoption_int(toolbar_status_size) < 1500) { + nsoption_set_int(toolbar_status_size, 6667); + } + + return NSERROR_OK; +} + static struct gui_browser_table nsgtk_browser_table = { .poll = nsgtk_poll, .schedule = nsgtk_schedule, @@ -1008,7 +1156,6 @@ static struct gui_browser_table nsgtk_browser_table = { int main(int argc, char** argv) { char *messages; - char *options; nserror ret; struct gui_table nsgtk_gui_table = { .browser = &nsgtk_browser_table, @@ -1019,11 +1166,21 @@ int main(int argc, char** argv) .search = nsgtk_search_table, }; - /* check home directory is available */ - nsgtk_check_homedir(); - + /* build the common resource path list */ respaths = nsgtk_init_resource("${HOME}/.netsurf/:${NETSURFRES}:"GTK_RESPATH":./gtk/res"); + /* Locate the correct user configuration directory path */ + ret = get_config_home(&nsgtk_config_home); + if (ret == NSERROR_NOT_FOUND) { + /* no config directory exists yet so try to create one */ + ret = create_config_home(&nsgtk_config_home); + } + if (ret != NSERROR_OK) { + LOG(("Unable to locate a configuration directory.")); + nsgtk_config_home = NULL; + } + + /* Initialise gtk */ gtk_init(&argc, &argv); /* initialise logging. Not fatal if it fails but not much we @@ -1031,21 +1188,18 @@ int main(int argc, char** argv) */ nslog_init(nslog_stream_configure, &argc, argv); - /* user options setup */ - ret = nsoption_init(set_defaults, &nsoptions, &nsoptions_default); + /* Initialise user options */ + ret = nsgtk_option_init(&argc, argv); if (ret != NSERROR_OK) { fprintf(stderr, "Options failed to initialise (%s)\n", messages_get_errorcode(ret)); return 1; } - options = filepath_find(respaths, "Choices"); - nsoption_read(options, nsoptions); - free(options); - nsoption_commandline(&argc, argv, nsoptions); - check_options(respaths); /* check user options */ - /* common initialisation */ + /* Obtain path to messages */ messages = filepath_find(respaths, "Messages"); + + /* core initialisation */ ret = netsurf_init(messages, &nsgtk_gui_table); free(messages); if (ret != NSERROR_OK) { diff --git a/gtk/gui.h b/gtk/gui.h index dc1f2b340..a9a98c6a0 100644 --- a/gtk/gui.h +++ b/gtk/gui.h @@ -52,11 +52,12 @@ extern struct glade_file_location_s *glade_file_location; extern char *languages_file_location; extern char *toolbar_indices_file_location; -extern char *options_file_location; /**< location where user options are written */ extern char *res_dir_location; -extern char *print_options_file_location; extern char *themelist_file_location; +/** Directory where all configuration files are held. */ +extern char *nsgtk_config_home; + extern GdkPixbuf *favicon_pixbuf; /* favicon default pixbuf */ extern char **respaths; /** resource search path vector */ diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c index c5dd524ff..436830bd3 100644 --- a/gtk/scaffolding.c +++ b/gtk/scaffolding.c @@ -25,6 +25,7 @@ #include #include +#include "utils/filepath.h" #include "utils/messages.h" #include "utils/url.h" #include "utils/log.h" @@ -863,9 +864,10 @@ MULTIHANDLER(print) GtkPrintOperation *print_op; GtkPageSetup *page_setup; - GtkPrintSettings *gtk_print_settings; + GtkPrintSettings *print_settings; GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR; - struct print_settings *settings; + struct print_settings *nssettings; + char *settings_fname; print_op = gtk_print_operation_new(); if (print_op == NULL) { @@ -874,14 +876,16 @@ MULTIHANDLER(print) } /* use previously saved settings if any */ - gtk_print_settings = gtk_print_settings_new_from_file( - print_options_file_location, NULL); - if (gtk_print_settings != NULL) { - gtk_print_operation_set_print_settings(print_op, - gtk_print_settings); - - /* We're not interested in the settings any more */ - g_object_unref(gtk_print_settings); + settings_fname = filepath_append(nsgtk_config_home, "Print"); + if (settings_fname != NULL) { + print_settings = gtk_print_settings_new_from_file(settings_fname, NULL); + if (print_settings != NULL) { + gtk_print_operation_set_print_settings(print_op, + print_settings); + + /* We're not interested in the settings any more */ + g_object_unref(print_settings); + } } content_to_print = bw->current_content; @@ -889,33 +893,40 @@ MULTIHANDLER(print) page_setup = gtk_print_run_page_setup_dialog(g->window, NULL, NULL); if (page_setup == NULL) { warn_user(messages_get("NoMemory"), 0); + free(settings_fname); g_object_unref(print_op); return TRUE; } gtk_print_operation_set_default_page_setup(print_op, page_setup); - settings = print_make_settings(PRINT_DEFAULT, NULL, &nsfont); + nssettings = print_make_settings(PRINT_DEFAULT, NULL, &nsfont); g_signal_connect(print_op, "begin_print", - G_CALLBACK(gtk_print_signal_begin_print), settings); + G_CALLBACK(gtk_print_signal_begin_print), nssettings); g_signal_connect(print_op, "draw_page", G_CALLBACK(gtk_print_signal_draw_page), NULL); g_signal_connect(print_op, "end_print", - G_CALLBACK(gtk_print_signal_end_print), settings); - if (content_get_type(bw->current_content) != CONTENT_TEXTPLAIN) + G_CALLBACK(gtk_print_signal_end_print), nssettings); + + if (content_get_type(bw->current_content) != CONTENT_TEXTPLAIN) { res = gtk_print_operation_run(print_op, GTK_PRINT_OPERATION_ACTION_PRINT_DIALOG, g->window, NULL); + } /* if the settings were used save them for future use */ - if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { - /* Don't ref the settings, as we don't want to own them */ - gtk_print_settings = gtk_print_operation_get_print_settings( - print_op); - - gtk_print_settings_to_file(gtk_print_settings, - print_options_file_location, NULL); + if (settings_fname != NULL) { + if (res == GTK_PRINT_OPERATION_RESULT_APPLY) { + /* Do not increment the settings reference */ + print_settings = + gtk_print_operation_get_print_settings(print_op); + + gtk_print_settings_to_file(print_settings, + settings_fname, + NULL); + } + free(settings_fname); } /* Our print_settings object is destroyed by the end print handler */ @@ -1249,6 +1260,7 @@ MULTIHANDLER(downloads) MULTIHANDLER(savewindowsize) { int x,y,w,h; + char *choices; gtk_window_get_position(g->window, &x, &y); gtk_window_get_size(g->window, &w, &h); @@ -1258,7 +1270,11 @@ MULTIHANDLER(savewindowsize) nsoption_set_int(window_x, x); nsoption_set_int(window_y, y); - nsoption_write(options_file_location, NULL, NULL); + choices = filepath_append(nsgtk_config_home, "Choices"); + if (choices != NULL) { + nsoption_write(choices, NULL, NULL); + free(choices); + } return TRUE; } diff --git a/utils/errors.h b/utils/errors.h index c38e4b9e2..4c02adb2d 100644 --- a/utils/errors.h +++ b/utils/errors.h @@ -37,6 +37,8 @@ typedef enum { NSERROR_NOT_FOUND, /**< Requested item not found */ + NSERROR_NOT_DIRECTORY, /**< Missing directory */ + NSERROR_SAVE_FAILED, /**< Failed to save data */ NSERROR_CLONE_FAILED, /**< Failed to clone handle */ @@ -69,7 +71,9 @@ typedef enum { NSERROR_BAD_CONTENT, /**< Bad Content */ - NSERROR_FRAME_DEPTH /**< Exceeded frame depth */ + NSERROR_FRAME_DEPTH, /**< Exceeded frame depth */ + + NSERROR_PERMISSION /**< Permission error */ } nserror; #endif diff --git a/utils/filepath.c b/utils/filepath.c index d088777e5..d82dfc627 100644 --- a/utils/filepath.c +++ b/utils/filepath.c @@ -202,7 +202,13 @@ filepath_generate(char * const *pathv, const char * const *langv) return respath; } -/* expand ${} in a string into environment variables */ +/** + * expand ${} in a string into environment variables. + * + * @param path The pathname to expand. + * @param pathlen The length of the path element. + * @return A string with the expanded path or NULL on empty expansion or error. + */ static char * expand_path(const char *path, int pathlen) { @@ -317,3 +323,80 @@ void filepath_free_strvec(char **pathv) } free(pathv); } + +/* exported interface documented in filepath.h */ +char *filepath_append(const char *path, const char *leaf) +{ + int dirname_len; + char *dirname; + + if ((path == NULL) || (leaf == NULL)) { + return NULL; + } + + dirname_len = strlen(path) + strlen(leaf) + 2; + dirname = malloc(dirname_len); + if (dirname != NULL) { + snprintf(dirname, dirname_len, "%s/%s", path, leaf); + } + + return dirname; +} + +/* exported interface documented in filepath.h */ +nserror filepath_mkdir_all(const char *fname) +{ + char *dname; + char *sep; + struct stat sb; + + dname = strdup(fname); + + sep = strrchr(dname, '/'); + if (sep == NULL) { + /* no directory separator path is just filename so its ok */ + free(dname); + return NSERROR_OK; + } + + *sep = 0; /* null terminate directory path */ + + if (stat(dname, &sb) == 0) { + free(dname); + if (S_ISDIR(sb.st_mode)) { + /* path to file exists and is a directory */ + return NSERROR_OK; + } + return NSERROR_NOT_DIRECTORY; + } + *sep = '/'; /* restore separator */ + + sep = dname; + while (*sep == '/') { + sep++; + } + while ((sep = strchr(sep, '/')) != NULL) { + *sep = 0; + if (stat(dname, &sb) != 0) { + if (mkdir(dname, S_IRWXU) != 0) { + /* could not create path element */ + free(dname); + return NSERROR_NOT_FOUND; + } + } else { + if (! S_ISDIR(sb.st_mode)) { + /* path element not a directory */ + free(dname); + return NSERROR_NOT_DIRECTORY; + } + } + *sep = '/'; /* restore separator */ + /* skip directory separators */ + while (*sep == '/') { + sep++; + } + } + + free(dname); + return NSERROR_OK; +} diff --git a/utils/filepath.h b/utils/filepath.h index ff3ebe90e..56e9eff74 100644 --- a/utils/filepath.h +++ b/utils/filepath.h @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -/** - * \file utils/filepath.h - * \brief Utility routines to obtain paths to file resources. +/** + * @file utils/filepath.h + * @brief Utility routines to obtain paths to file resources. */ #ifndef _NETSURF_UTILS_FILEPATH_H_ @@ -26,8 +26,10 @@ #include +#include "utils/errors.h" -/** Create a normalised file name. +/** + * Create a normalised file name. * * If the file described by the format exists and is accessible the * normalised path is placed in str and a pointer to str returned @@ -43,14 +45,16 @@ char *filepath_vsfindfile(char *str, const char *format, va_list ap); -/** Create a normalised file name. +/** + * Create a normalised file name. * * Similar to vsfindfile but takes variadic (printf like) parameters */ char *filepath_sfindfile(char *str, const char *format, ...); -/** Create a normalised file name. +/** + * Create a normalised file name. * * Similar to sfindfile but allocates its own storage for the * returned string. The caller must free this sorage. @@ -58,7 +62,8 @@ char *filepath_sfindfile(char *str, const char *format, ...); char *filepath_findfile(const char *format, ...); -/** Searches an array of resource paths for a file. +/** + * Searches an array of resource paths for a file. * * Iterates through a vector of resource paths and returns the * normalised file name of the first acessible file or NULL if no file @@ -72,7 +77,8 @@ char *filepath_findfile(const char *format, ...); char *filepath_sfind(char **respathv, char *filepath, const char *filename); -/** Searches an array of resource paths for a file. +/** + * Searches an array of resource paths for a file. * * Similar to filepath_sfind except it allocates its own storage for * the returned string. The caller must free this sorage. @@ -80,7 +86,8 @@ char *filepath_sfind(char **respathv, char *filepath, const char *filename); char *filepath_find(char **respathv, const char *filename); -/** Searches an array of resource paths for a file optionally forcing a default. +/** + * Searches an array of resource paths for a file optionally forcing a default. * * Similar to filepath_sfind except if no resource is found the default * is used as an additional path element to search, if that still @@ -91,7 +98,8 @@ char *filepath_sfinddef(char **respathv, char *filepath, const char *filename, const char *def); -/** Merge two string vectors into a resource search path vector. +/** + * Merge two string vectors into a resource search path vector. * * @param pathv A string vector containing path elemets to scan. * @param langv A string vector containing language names to enumerate. @@ -101,7 +109,8 @@ char *filepath_sfinddef(char **respathv, char *filepath, const char *filename, char **filepath_generate(char * const *pathv, const char * const *langv); -/** Convert a colon separated list of path elements into a string vector. +/** + * Convert a colon separated list of path elements into a string vector. * * @param path A colon separated path. * @return A pointer to a NULL terminated string vector of valid @@ -110,10 +119,32 @@ char **filepath_generate(char * const *pathv, const char * const *langv); char **filepath_path_to_strvec(const char *path); -/** Free a string vector +/** + * Free a string vector. * * Free a string vector allocated by filepath_path_to_strvec */ void filepath_free_strvec(char **pathv); + +/** + * generate a new filename from a path and leaf. + * + * @param path The base path. + * @param leaf The leaf to add. + * @return The combined path in a new string must be freed by caller + * or NULL on failiure to allocte memory. + */ +char *filepath_append(const char *path, const char *leaf); + + +/** + * Ensure that all directory elements needed to store a filename exist. + * + * @param fname The filename to ensure the path to exists. + * @return NSERROR_OK on success or error code on failure. + */ +nserror filepath_mkdir_all(const char *fname); + + #endif /* _NETSURF_UTILS_FILEPATH_H_ */ -- cgit v1.2.3