summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk/dialogs/preferences.c31
-rw-r--r--gtk/gui.c342
-rw-r--r--gtk/gui.h5
-rw-r--r--gtk/scaffolding.c60
-rw-r--r--utils/errors.h6
-rw-r--r--utils/filepath.c85
-rw-r--r--utils/filepath.h55
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 <stdint.h>
#include <math.h>
+#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 <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
+#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 <http://www.gnu.org/licenses/>.
*/
-/**
- * \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 <stdarg.h>
+#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_ */