diff --git a/amiga/gui.c b/amiga/gui.c index 47a7fbea9..9fce7b0e0 100644 --- a/amiga/gui.c +++ b/amiga/gui.c @@ -36,6 +36,7 @@ #include "utils/utf8.h" #include "utils/utils.h" #include "utils/url.h" +#include "utils/file.h" #include "content/fetchers/resource.h" /* NetSurf Amiga platform includes */ @@ -221,33 +222,126 @@ static void gui_window_place_caret(struct gui_window *g, int x, int y, int heigh nsoptions[NSOPTION_##OPTION].value.i = VALUE; \ nsoptions_default[NSOPTION_##OPTION].value.i = VALUE -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ -static char *filename_from_path(char *path) +/** + * Generate a posix path from one or more component elemnts. + * + * If a string is allocated it must be freed by the caller. + * + * @param[in,out] str pointer to string pointer if this is NULL enough + * storage will be allocated for the complete path. + * @param[in,out] size The size of the space available if \a str not + * NULL on input and if not NULL set to the total + * output length on output. + * @param[in] nemb The number of elements. + * @param[in] ... The elements of the path as string pointers. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ +static nserror amiga_mkpath(char **str, size_t *size, size_t nelm, va_list ap) { - return strdup(FilePart(path)); + const char *elm[16]; + size_t elm_len[16]; + size_t elm_idx; + char *fname; + size_t fname_len = 0; + char *curp; + + /* check the parameters are all sensible */ + if ((nelm == 0) || (nelm > 16)) { + return NSERROR_BAD_PARAMETER; + } + if ((*str != NULL) && (size == NULL)) { + /* if the caller is providing the buffer they must say + * how much space is available. + */ + return NSERROR_BAD_PARAMETER; + } + + /* calculate how much storage we need for the complete path + * with all the elements. + */ + for (elm_idx = 0; elm_idx < nelm; elm_idx++) { + elm[elm_idx] = va_arg(ap, const char *); + elm_len[elm_idx] = strlen(elm[elm_idx]); + fname_len += elm_len[elm_idx]; + } + fname_len += nelm; /* allow for separators and terminator */ + + /* ensure there is enough space */ + fname = *str; + if (fname != NULL) { + if (fname_len > *size) { + return NSERROR_NOSPACE; + } + } else { + fname = malloc(fname_len); + if (fname == NULL) { + return NSERROR_NOMEM; + } + } + + /* copy the first element complete */ + memmove(fname, elm[0], elm_len[0]); + fname[elm_len[0]] = 0; + + /* add the remaining elements */ + for (elm_idx = 1; elm_idx < nelm; elm_idx++) { + if (!AddPart(fname, elm[elm_idx], fname_len)) { + break; + } + } + + *str = fname; + if (size != NULL) { + *size = fname_len; + } + + return NSERROR_OK; } /** - * Add a path component/filename to an existing path + * Get the basename of a file using posix path handling. * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success + * This gets the last element of a path and returns it. + * + * @param[in] path The path to extract the name from. + * @param[in,out] str Pointer to string pointer if this is NULL enough + * storage will be allocated for the path element. + * @param[in,out] size The size of the space available if \a + * str not NULL on input and set to the total + * output length on output. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. */ - -static bool path_add_part(char *path, int length, const char *newpart) +static nserror amiga_basename(const char *path, char **str, size_t *size) { - if(AddPart(path, newpart, length)) return true; - else return false; + const char *leafname; + char *fname; + + if (path == NULL) { + return NSERROR_BAD_PARAMETER; + } + + leafname = FilePart(path); + if (leafname == NULL) { + return NSERROR_BAD_PARAMETER; + } + + fname = strdup(leafname); + if (fname == NULL) { + return NSERROR_NOMEM; + } + + *str = fname; + if (size != NULL) { + *size = strlen(fname); + } + return NSERROR_OK; } + + STRPTR ami_locale_langs(void) { struct Locale *locale; @@ -290,12 +384,12 @@ bool ami_gui_map_filename(char **remapped, const char *path, const char *file, c { BPTR fh = 0; char mapfile[1024]; + size_t mapfile_size = 1024; char buffer[1024]; char *realfname; bool found = false; - strcpy(mapfile, path); - path_add_part(mapfile, 1024, map); + amiga_mkpath(&mapfile, &mapfile_size, 2, path, map); if(fh = FOpen(mapfile, MODE_OLDFILE, 0)) { @@ -331,9 +425,10 @@ bool ami_gui_check_resource(char *fullpath, const char *file) bool found = false; char *remapped; BPTR lock = 0; + size_t fullpath_len = 1024; ami_gui_map_filename(&remapped, fullpath, file, "Resource.map"); - path_add_part(fullpath, 1024, remapped); + amiga_mkpath(&fullpath, &fullpath_len, 2, fullpath, remapped); LOG(("Checking for %s", fullpath)); @@ -356,6 +451,7 @@ bool ami_locate_resource(char *fullpath, const char *file) BPTR lock = 0; bool found = false; char *remapped; + size_t fullpath_len = 1024; /* Check NetSurf user data area first */ @@ -383,7 +479,7 @@ bool ami_locate_resource(char *fullpath, const char *file) { ami_gui_map_filename(&remapped, "PROGDIR:Resources", locale->loc_PrefLanguages[i], "LangNames"); - path_add_part(fullpath, 1024, remapped); + amiga_mkpath(&fullpath, &fullpath_len, 2 fullpath, remapped); found = ami_gui_check_resource(fullpath, file); } @@ -5097,9 +5193,13 @@ static struct gui_window_table amiga_window_table = { .save_link = gui_window_save_link, }; +/* amiga file handling operations */ +static struct gui_file_table amiga_file_table = { + .mkpath = amiga_mkpath, + .basename = amiga_basename, +}; + static struct gui_fetch_table amiga_fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, @@ -5136,6 +5236,7 @@ int main(int argc, char** argv) .clipboard = amiga_clipboard_table, .download = amiga_download_table, .fetch = &amiga_fetch_table, + .file = &amiga_file_table, .utf8 = amiga_utf8_table, .search = amiga_search_table, }; diff --git a/atari/gui.c b/atari/gui.c index c275bfdc4..0979f2974 100644 --- a/atari/gui.c +++ b/atari/gui.c @@ -114,28 +114,6 @@ short aes_msg_out[8]; bool gui_window_get_scroll(struct gui_window *w, int *sx, int *sy); static void gui_window_set_url(struct gui_window *w, const char *url); -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ -static char *filename_from_path(char *path) -{ - char *leafname; - - leafname = strrchr(path, '\\'); - if( !leafname ) - leafname = strrchr(path, '/'); - if (!leafname) - leafname = path; - else - leafname += 1; - - return strdup(leafname); -} - - static void gui_poll(bool active) { @@ -1071,8 +1049,6 @@ static struct gui_clipboard_table atari_clipboard_table = { }; static struct gui_fetch_table atari_fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, diff --git a/atari/misc.c b/atari/misc.c index ca9e993d9..a8eff288b 100755 --- a/atari/misc.c +++ b/atari/misc.c @@ -70,24 +70,6 @@ void die(const char *error) exit(1); } -/** - * Add a path component/filename to an existing path - * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success - */ - -bool path_add_part(char *path, int length, const char *newpart) -{ - if(path[strlen(path) - 1] != '/') - strncat(path, "/", length); - - strncat(path, newpart, length); - - return true; -} struct gui_window * find_guiwin_by_aes_handle(short handle){ @@ -242,25 +224,17 @@ hlcache_handle *load_icon(const char *name, hlcache_handle_callback cb, if (!strncmp(name, "file://", 7)) { icon_url = name; } else { - char *native_path; + char *native_path = NULL; if (icons_dir == NULL) return NULL; - /* path + separator + leafname + '\0' */ - len = strlen(icons_dir) + 1 + strlen(name) + 1; - native_path = malloc(len); - if (native_path == NULL) { - LOG(("malloc failed")); - warn_user("NoMemory", 0); + err = netsurf_mkpath(&native_path, NULL, 2, icons_dir, name); + if (err != NSERROR_OK) { + warn_user(messages_get_errorcode(err)); return NULL; } - /* Build native path */ - memcpy(native_path, icons_dir, - strlen(icons_dir) + 1); - path_add_part(native_path, len, name); - /* Convert native path to URL */ url = path_to_url(native_path); diff --git a/atari/misc.h b/atari/misc.h index 1bb5e8131..8d1719ce8 100755 --- a/atari/misc.h +++ b/atari/misc.h @@ -66,6 +66,4 @@ const char * file_select(const char * title, const char * name); */ long nkc_to_input_key(short nkc, long * ucs4_out); -bool path_add_part(char *path, int length, const char *newpart); - #endif diff --git a/beos/gui.cpp b/beos/gui.cpp index 365a35642..21bbf11f1 100644 --- a/beos/gui.cpp +++ b/beos/gui.cpp @@ -993,44 +993,6 @@ static void *myrealloc(void *ptr, size_t len, void *pw) return realloc(ptr, len); } -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ - -static char *filename_from_path(char *path) -{ - char *leafname; - - leafname = strrchr(path, '/'); - if (!leafname) - leafname = path; - else - leafname += 1; - - return strdup(leafname); -} - -/** - * Add a path component/filename to an existing path - * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success - */ - -static bool path_add_part(char *path, int length, const char *newpart) -{ - if(path[strlen(path) - 1] != '/') - strncat(path, "/", length); - - strncat(path, newpart, length); - - return true; -} static struct gui_clipboard_table beos_clipboard_table = { gui_get_clipboard, @@ -1038,8 +1000,6 @@ static struct gui_clipboard_table beos_clipboard_table = { }; static struct gui_fetch_table beos_fetch_table = { - filename_from_path, - path_add_part, fetch_filetype, path_to_url, url_to_path, diff --git a/cocoa/fetch.m b/cocoa/fetch.m index 70d3805e1..8cc2cb966 100644 --- a/cocoa/fetch.m +++ b/cocoa/fetch.m @@ -107,20 +107,6 @@ static char *path_to_url(const char *path) absoluteString] UTF8String] ); } -static char *filename_from_path(char *path) -{ - return strdup( [[[NSString stringWithUTF8String: path] lastPathComponent] UTF8String] ); -} - -static bool path_add_part(char *path, int length, const char *newpart) -{ - NSString *newPath = [[NSString stringWithUTF8String: path] stringByAppendingPathComponent: [NSString stringWithUTF8String: newpart]]; - - strncpy( path, [newPath UTF8String], length ); - - return true; -} - static nsurl *gui_get_resource_url(const char *path) { nsurl *url = NULL; @@ -131,8 +117,6 @@ static nsurl *gui_get_resource_url(const char *path) } static struct gui_fetch_table fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, diff --git a/content/fetchers/curl.c b/content/fetchers/curl.c index 612b77d0b..80ac5ec89 100644 --- a/content/fetchers/curl.c +++ b/content/fetchers/curl.c @@ -50,6 +50,7 @@ #include "utils/utils.h" #include "utils/ring.h" #include "utils/useragent.h" +#include "utils/file.h" #include "content/fetch.h" #include "content/fetchers/curl.h" @@ -1280,15 +1281,15 @@ fetch_curl_post_convert(const struct fetch_multipart_data *control) { struct curl_httppost *post = 0, *last = 0; CURLFORMcode code; + nserror ret; for (; control; control = control->next) { if (control->file) { - char *leafname = 0; - - leafname = guit->fetch->filename_from_path(control->value); - - if (leafname == NULL) + char *leafname = NULL; + ret = guit->file->basename(control->value, &leafname, NULL); + if (ret != NSERROR_OK) { continue; + } /* We have to special case filenames of "", so curl * a) actually attempts the fetch and diff --git a/content/fetchers/file.c b/content/fetchers/file.c index 00d5cc5f1..93e3bf84c 100644 --- a/content/fetchers/file.c +++ b/content/fetchers/file.c @@ -18,6 +18,8 @@ /* file: URL handling. Based on the data fetcher by Rob Kendrick */ +#include "utils/config.h" + #include #include #include @@ -34,18 +36,12 @@ #include #include -#include "utils/config.h" - #ifdef HAVE_MMAP #include #endif #include -#include "content/dirlist.h" -#include "content/fetch.h" -#include "content/fetchers/file.h" -#include "content/urldb.h" #include "desktop/netsurf.h" #include "desktop/gui_factory.h" #include "utils/corestrings.h" @@ -56,6 +52,12 @@ #include "utils/url.h" #include "utils/utils.h" #include "utils/ring.h" +#include "utils/file.h" + +#include "content/dirlist.h" +#include "content/fetch.h" +#include "content/urldb.h" +#include "content/fetchers/file.h" /* Maximum size of read buffer */ #define FETCH_FILE_MAX_BUF_SIZE (1024 * 1024) @@ -489,6 +491,97 @@ static char *gen_nice_title(char *path) return title; } +/** + * generate an output row of the directory listing. + * + * @param ent current directory entry. + */ +static nserror +process_dir_ent(struct fetch_file_context *ctx, + struct dirent *ent, + bool even, + char *buffer, + size_t buffer_len) +{ + nserror ret; + char *path; /* url for list entries */ + char *urlpath = NULL; /* buffer for leaf entry path */ + struct stat ent_stat; /* stat result of leaf entry */ + char datebuf[64]; /* buffer for date text */ + char timebuf[64]; /* buffer for time text */ + + /* skip hidden files */ + if (ent->d_name[0] == '.') { + return NSERROR_BAD_PARAMETER; + } + + ret = netsurf_mkpath(&urlpath, NULL, 2, ctx->path, ent->d_name); + if (ret != NSERROR_OK) { + return ret; + } + + if (stat(urlpath, &ent_stat) != 0) { + ent_stat.st_mode = 0; + datebuf[0] = 0; + timebuf[0] = 0; + } else { + /* Get date in output format */ + if (strftime((char *)&datebuf, sizeof datebuf, "%a %d %b %Y", + localtime(&ent_stat.st_mtime)) == 0) { + datebuf[0] = '-'; + datebuf[1] = 0; + } + + /* Get time in output format */ + if (strftime((char *)&timebuf, sizeof timebuf, "%H:%M", + localtime(&ent_stat.st_mtime)) == 0) { + timebuf[0] = '-'; + timebuf[1] = 0; + } + } + + if ((path = guit->fetch->path_to_url(urlpath)) == NULL) { + free(urlpath); + return NSERROR_NOMEM; + } + + if (S_ISREG(ent_stat.st_mode)) { + /* regular file */ + dirlist_generate_row(even, + false, + path, + ent->d_name, + guit->fetch->filetype(urlpath), + ent_stat.st_size, + datebuf, timebuf, + buffer, buffer_len); + } else if (S_ISDIR(ent_stat.st_mode)) { + /* directory */ + dirlist_generate_row(even, + true, + path, + ent->d_name, + messages_get("FileDirectory"), + -1, + datebuf, timebuf, + buffer, buffer_len); + } else { + /* something else */ + dirlist_generate_row(even, + false, + path, + ent->d_name, + "", + -1, + datebuf, timebuf, + buffer, buffer_len); + } + + free(path); + free(urlpath); + + return NSERROR_OK; +} static void fetch_file_process_dir(struct fetch_file_context *ctx, struct stat *fdstat) @@ -499,13 +592,7 @@ static void fetch_file_process_dir(struct fetch_file_context *ctx, char *title; /* pretty printed title */ nserror err; /* result from url routines */ nsurl *up; /* url of parent */ - char *path; /* url for list entries */ - struct stat ent_stat; /* stat result of leaf entry */ - char datebuf[64]; /* buffer for date text */ - char timebuf[64]; /* buffer for time text */ - char urlpath[PATH_MAX]; /* buffer for leaf entry path */ - struct dirent *ent; /* current directory entry */ struct dirent **listing = NULL; /* directory entry listing */ int i; /* directory entry index */ int n; /* number of directory entries */ @@ -570,78 +657,17 @@ static void fetch_file_process_dir(struct fetch_file_context *ctx, goto fetch_file_process_dir_aborted; for (i = 0; i < n; i++) { - ent = listing[i]; - if (ent->d_name[0] == '.') - continue; + err = process_dir_ent(ctx, listing[i], even, buffer, + sizeof(buffer)); - strncpy(urlpath, ctx->path, sizeof urlpath); - if (guit->fetch->path_add_part(urlpath, sizeof urlpath, - ent->d_name) == false) - continue; + if (err == NSERROR_OK) { + msg.data.header_or_data.len = strlen(buffer); + if (fetch_file_send_callback(&msg, ctx)) + goto fetch_file_process_dir_aborted; - if (stat(urlpath, &ent_stat) != 0) { - ent_stat.st_mode = 0; - datebuf[0] = 0; - timebuf[0] = 0; - } else { - /* Get date in output format */ - if (strftime((char *)&datebuf, sizeof datebuf, - "%a %d %b %Y", - localtime(&ent_stat.st_mtime)) == 0) { - strncpy(datebuf, "-", sizeof datebuf); - } - - /* Get time in output format */ - if (strftime((char *)&timebuf, sizeof timebuf, - "%H:%M", - localtime(&ent_stat.st_mtime)) == 0) { - strncpy(timebuf, "-", sizeof timebuf); - } + even = !even; } - - if((path = guit->fetch->path_to_url(urlpath)) == NULL) - continue; - - if (S_ISREG(ent_stat.st_mode)) { - /* regular file */ - dirlist_generate_row(even, - false, - path, - ent->d_name, - guit->fetch->filetype(urlpath), - ent_stat.st_size, - datebuf, timebuf, - buffer, sizeof(buffer)); - } else if (S_ISDIR(ent_stat.st_mode)) { - /* directory */ - dirlist_generate_row(even, - true, - path, - ent->d_name, - messages_get("FileDirectory"), - -1, - datebuf, timebuf, - buffer, sizeof(buffer)); - } else { - /* something else */ - dirlist_generate_row(even, - false, - path, - ent->d_name, - "", - -1, - datebuf, timebuf, - buffer, sizeof(buffer)); - } - - free(path); - - msg.data.header_or_data.len = strlen(buffer); - if (fetch_file_send_callback(&msg, ctx)) - goto fetch_file_process_dir_aborted; - - even = !even; } /* directory listing bottom */ diff --git a/desktop/gui.h b/desktop/gui.h index 5f4ba1cdb..0d4135a93 100644 --- a/desktop/gui.h +++ b/desktop/gui.h @@ -68,6 +68,7 @@ struct ssl_cert_info; struct hlcache_handle; struct download_context; struct nsurl; +struct gui_file_table; typedef struct nsnsclipboard_styles { size_t start; /**< Start of run */ @@ -317,28 +318,6 @@ struct gui_clipboard_table { struct gui_fetch_table { /* Mandantory entries */ - /** - * Return the filename part of a full path - * - * @note used in curl fetcher - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ - char *(*filename_from_path)(char *path); - - /** - * Add a path component/filename to an existing path - * - * @note used in save complete and file fetcher - * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success - */ - bool (*path_add_part)(char *path, int length, const char *newpart); - /** * Determine the MIME type of a local file. * @@ -398,6 +377,7 @@ struct gui_fetch_table { }; + /** * User interface utf8 characterset conversion routines. */ @@ -567,6 +547,15 @@ struct gui_table { /** Fetcher table */ struct gui_fetch_table *fetch; + /** + * File table + * + * Provides file and filename operations to the core. The + * table is optional and may be NULL in which case the default + * posix compliant operations will be used. + */ + struct gui_file_table *file; + /** * UTF8 table. * diff --git a/desktop/gui_factory.c b/desktop/gui_factory.c index ecedf417b..8e3ce178d 100644 --- a/desktop/gui_factory.c +++ b/desktop/gui_factory.c @@ -19,6 +19,7 @@ #include "content/hlcache.h" #include "desktop/download.h" #include "desktop/gui_factory.h" +#include "utils/file.h" /** The global GUI interface table */ struct gui_table *guit = NULL; @@ -418,12 +419,6 @@ static nserror verify_fetch_register(struct gui_fetch_table *gft) } /* check the mandantory fields are set */ - if (gft->filename_from_path == NULL) { - return NSERROR_BAD_PARAMETER; - } - if (gft->path_add_part == NULL) { - return NSERROR_BAD_PARAMETER; - } if (gft->filetype == NULL) { return NSERROR_BAD_PARAMETER; } @@ -446,6 +441,25 @@ static nserror verify_fetch_register(struct gui_fetch_table *gft) return NSERROR_OK; } +/** verify file table is valid */ +static nserror verify_file_register(struct gui_file_table *gft) +{ + /* check table is present */ + if (gft == NULL) { + return NSERROR_BAD_PARAMETER; + } + + /* check the mandantory fields are set */ + if (gft->mkpath == NULL) { + return NSERROR_BAD_PARAMETER; + } + if (gft->basename == NULL) { + return NSERROR_BAD_PARAMETER; + } + + return NSERROR_OK; +} + static void gui_default_quit(void) { } @@ -559,6 +573,15 @@ nserror gui_factory_register(struct gui_table *gt) return err; } + /* file table */ + if (gt->file == NULL) { + gt->file = default_file_table; + } + err = verify_file_register(gt->file); + if (err != NSERROR_OK) { + return err; + } + /* download table */ if (gt->download == NULL) { /* set default download table */ diff --git a/desktop/save_complete.c b/desktop/save_complete.c index 99b1ac4b7..782e35841 100644 --- a/desktop/save_complete.c +++ b/desktop/save_complete.c @@ -21,8 +21,6 @@ * Save HTML document with dependencies (implementation). */ -#include "utils/config.h" - #include #include #include @@ -30,21 +28,23 @@ #include #include #include - #include -#include "content/content.h" -#include "content/hlcache.h" -#include "css/css.h" -#include "desktop/save_complete.h" -#include "desktop/gui_factory.h" -#include "render/box.h" -#include "render/html.h" +#include "utils/config.h" #include "utils/corestrings.h" #include "utils/log.h" #include "utils/nsurl.h" #include "utils/utf8.h" #include "utils/utils.h" +#include "utils/file.h" +#include "content/content.h" +#include "content/hlcache.h" +#include "css/css.h" +#include "render/box.h" +#include "render/html.h" + +#include "desktop/gui_factory.h" +#include "desktop/save_complete.h" regex_t save_complete_import_re; @@ -143,19 +143,19 @@ static bool save_complete_save_buffer(save_complete_ctx *ctx, const char *leafname, const char *data, size_t data_len, lwc_string *mime_type) { + nserror ret; FILE *fp; - bool error; - char fullpath[PATH_MAX]; + char *fname = NULL; - strncpy(fullpath, ctx->path, sizeof fullpath); - error = guit->fetch->path_add_part(fullpath, sizeof fullpath, leafname); - if (error == false) { - warn_user("NoMemory", NULL); + ret = netsurf_mkpath(&fname, NULL, 2, ctx->path, leafname); + if (ret != NSERROR_OK) { + warn_user("NoMemory", 0); return false; } - fp = fopen(fullpath, "wb"); + fp = fopen(fname, "wb"); if (fp == NULL) { + free(fname); LOG(("fopen(): errno = %i", errno)); warn_user("SaveError", strerror(errno)); return false; @@ -165,8 +165,10 @@ static bool save_complete_save_buffer(save_complete_ctx *ctx, fclose(fp); - if (ctx->set_type != NULL) - ctx->set_type(fullpath, mime_type); + if (ctx->set_type != NULL) { + ctx->set_type(fname, mime_type); + } + free(fname); return true; } @@ -1034,29 +1036,30 @@ static bool save_complete_node_handler(dom_node *node, static bool save_complete_save_html_document(save_complete_ctx *ctx, hlcache_handle *c, bool index) { - bool error; + nserror ret; FILE *fp; + char *fname = NULL; dom_document *doc; lwc_string *mime_type; char filename[32]; - char fullpath[PATH_MAX]; - strncpy(fullpath, ctx->path, sizeof fullpath); - - if (index) + if (index) { snprintf(filename, sizeof filename, "index"); - else + } else { snprintf(filename, sizeof filename, "%p", c); + } - error = guit->fetch->path_add_part(fullpath, sizeof fullpath, filename); - if (error == false) { + ret = netsurf_mkpath(&fname, NULL, 2, ctx->path, filename); + if (ret != NSERROR_OK) { warn_user("NoMemory", NULL); return false; } - fp = fopen(fullpath, "wb"); + fp = fopen(fname, "wb"); if (fp == NULL) { - warn_user("NoMemory", NULL); + free(fname); + LOG(("fopen(): errno = %i", errno)); + warn_user("SaveError", strerror(errno)); return false; } @@ -1068,6 +1071,7 @@ static bool save_complete_save_html_document(save_complete_ctx *ctx, if (save_complete_libdom_treewalk((dom_node *) doc, save_complete_node_handler, ctx) == false) { + free(fname); warn_user("NoMemory", 0); fclose(fp); return false; @@ -1078,10 +1082,11 @@ static bool save_complete_save_html_document(save_complete_ctx *ctx, mime_type = content_get_mime_type(c); if (mime_type != NULL) { if (ctx->set_type != NULL) - ctx->set_type(fullpath, mime_type); + ctx->set_type(fname, mime_type); lwc_string_unref(mime_type); } + free(fname); return true; } @@ -1119,19 +1124,18 @@ static bool save_complete_save_html(save_complete_ctx *ctx, hlcache_handle *c, static bool save_complete_inventory(save_complete_ctx *ctx) { + nserror ret; FILE *fp; - bool error; + char *fname = NULL; save_complete_entry *entry; - char fullpath[PATH_MAX]; - strncpy(fullpath, ctx->path, sizeof fullpath); - error = guit->fetch->path_add_part(fullpath, sizeof fullpath, "Inventory"); - if (error == false) { - warn_user("NoMemory", NULL); + ret = netsurf_mkpath(&fname, NULL, 2, ctx->path, "Inventory"); + if (ret != NSERROR_OK) { return false; } - fp = fopen(fullpath, "w"); + fp = fopen(fname, "w"); + free(fname); if (fp == NULL) { LOG(("fopen(): errno = %i", errno)); warn_user("SaveError", strerror(errno)); diff --git a/framebuffer/fetch.c b/framebuffer/fetch.c index c39ab5819..7c8dcc589 100644 --- a/framebuffer/fetch.c +++ b/framebuffer/fetch.c @@ -34,42 +34,6 @@ #include "framebuffer/findfile.h" #include "framebuffer/fetch.h" -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ -static char *filename_from_path(char *path) -{ - char *leafname; - - leafname = strrchr(path, '/'); - if (!leafname) - leafname = path; - else - leafname += 1; - - return strdup(leafname); -} - -/** - * Add a path component/filename to an existing path - * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success - */ -static bool path_add_part(char *path, int length, const char *newpart) -{ - if(path[strlen(path) - 1] != '/') - strncat(path, "/", length); - - strncat(path, newpart, length); - - return true; -} /** * Convert a pathname to a file: URL. @@ -183,8 +147,6 @@ static const char *fetch_filetype(const char *unix_path) /* table for fetch operations */ static struct gui_fetch_table fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, diff --git a/gtk/dialogs/preferences.c b/gtk/dialogs/preferences.c index fcf30c8fa..d36d0c68b 100644 --- a/gtk/dialogs/preferences.c +++ b/gtk/dialogs/preferences.c @@ -19,12 +19,12 @@ #include #include -#include "utils/filepath.h" -#include "utils/log.h" #include "utils/utils.h" #include "utils/messages.h" -#include "desktop/browser.h" #include "utils/nsoption.h" +#include "utils/file.h" +#include "utils/log.h" +#include "desktop/browser.h" #include "desktop/searchweb.h" #include "gtk/compat.h" @@ -1002,10 +1002,10 @@ nsgtk_preferences_fileChooserDownloads_realize(GtkWidget *widget, G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_response(GtkDialog *dlg, gint resid) { - char *choices; + char *choices = NULL; if (resid == GTK_RESPONSE_CLOSE) { - choices = filepath_append(nsgtk_config_home, "Choices"); + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); if (choices != NULL) { nsoption_write(choices, NULL, NULL); free(choices); @@ -1018,9 +1018,9 @@ G_MODULE_EXPORT gboolean nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, struct ppref *priv) { - char *choices; + char *choices = NULL; - choices = filepath_append(nsgtk_config_home, "Choices"); + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); if (choices != NULL) { nsoption_write(choices, NULL, NULL); free(choices); @@ -1037,9 +1037,9 @@ nsgtk_preferences_dialogPreferences_deleteevent(GtkDialog *dlg, G_MODULE_EXPORT void nsgtk_preferences_dialogPreferences_destroy(GtkDialog *dlg, struct ppref *priv) { - char *choices; + char *choices = NULL; - choices = filepath_append(nsgtk_config_home, "Choices"); + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); if (choices != NULL) { nsoption_write(choices, NULL, NULL); free(choices); diff --git a/gtk/fetch.c b/gtk/fetch.c index 8db102ce8..a76ad9f4d 100644 --- a/gtk/fetch.c +++ b/gtk/fetch.c @@ -226,45 +226,6 @@ const char *fetch_filetype(const char *unix_path) return type; } -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ -static char *filename_from_path(char *path) -{ - char *leafname; - - leafname = strrchr(path, '/'); - if (!leafname) { - leafname = path; - } else { - leafname += 1; - } - - return strdup(leafname); -} - -/** - * Add a path component/filename to an existing path - * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success - */ -static bool path_add_part(char *path, int length, const char *newpart) -{ - if (path[strlen(path) - 1] != '/') { - strncat(path, "/", length); - } - - strncat(path, newpart, length); - - return true; -} - char *path_to_url(const char *path) { int urllen; @@ -337,14 +298,11 @@ static nsurl *gui_get_resource_url(const char *path) } static struct gui_fetch_table fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, .get_resource_url = gui_get_resource_url, - }; struct gui_fetch_table *nsgtk_fetch_table = &fetch_table; diff --git a/gtk/gui.c b/gtk/gui.c index 3147bf1b8..bae3568c6 100644 --- a/gtk/gui.c +++ b/gtk/gui.c @@ -55,6 +55,14 @@ #include "desktop/textinput.h" #include "desktop/tree.h" #include "css/utils.h" +#include "render/form.h" +#include "utils/filepath.h" +#include "utils/log.h" +#include "utils/messages.h" +#include "utils/url.h" +#include "utils/utf8.h" +#include "utils/utils.h" +#include "utils/file.h" #include "gtk/compat.h" #include "gtk/completion.h" @@ -71,13 +79,6 @@ #include "gtk/selection.h" #include "gtk/search.h" -#include "render/form.h" -#include "utils/filepath.h" -#include "utils/log.h" -#include "utils/messages.h" -#include "utils/url.h" -#include "utils/utf8.h" -#include "utils/utils.h" char *toolbar_indices_file_location; char *res_dir_location; @@ -253,33 +254,37 @@ static nserror set_defaults(struct nsoption_s *defaults) char *fname; /* cookie file default */ - fname = filepath_append(nsgtk_config_home, "Cookies"); + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies"); if (fname != NULL) { nsoption_setnull_charp(cookie_file, fname); } /* cookie jar default */ - fname = filepath_append(nsgtk_config_home, "Cookies"); + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Cookies"); if (fname != NULL) { nsoption_setnull_charp(cookie_jar, fname); } /* url database default */ - fname = filepath_append(nsgtk_config_home, "URLs"); + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "URLs"); if (fname != NULL) { nsoption_setnull_charp(url_file, fname); } /* bookmark database default */ - fname = filepath_append(nsgtk_config_home, "Hotlist"); + fname = NULL; + netsurf_mkpath(&fname, NULL, 2, nsgtk_config_home, "Hotlist"); if (fname != NULL) { nsoption_setnull_charp(hotlist_path, fname); } /* download directory default */ - fname = filepath_append(getenv("HOME"), ""); + fname = getenv("HOME"); if (fname != NULL) { - nsoption_setnull_charp(downloads_directory, fname); + nsoption_setnull_charp(downloads_directory, strdup(fname)); } /* default path to certificates */ @@ -955,12 +960,12 @@ static nserror check_dirname(const char *path, const char *leaf, char **dirname_out) { nserror ret; - char *dirname; + char *dirname = NULL; struct stat dirname_stat; - dirname = filepath_append(path, leaf); - if (dirname == NULL) { - return NSERROR_NOMEM; + ret = netsurf_mkpath(&dirname, NULL, 2, path, leaf); + if (ret != NSERROR_OK) { + return ret; } /* ensure access is possible and the entry is actualy @@ -1075,14 +1080,14 @@ static nserror create_config_home(char **config_home_out) return NSERROR_NOT_DIRECTORY; } - config_home = filepath_append(home_dir, ".config/netsurf/"); - if (config_home == NULL) { - return NSERROR_NOMEM; + ret = netsurf_mkpath(&config_home, NULL, 2, home_dir, ".config/netsurf/"); + if (ret != NSERROR_OK) { + return ret; } } else { - config_home = filepath_append(xdg_config_dir, "netsurf/"); - if (config_home == NULL) { - return NSERROR_NOMEM; + ret = netsurf_mkpath(&config_home, NULL, 2, xdg_config_dir, "netsurf/"); + if (ret != NSERROR_OK) { + return ret; } } @@ -1113,8 +1118,8 @@ static nserror nsgtk_option_init(int *pargc, char** argv) } /* Attempt to load the user choices */ - choices = filepath_append(nsgtk_config_home, "Choices"); - if (choices != NULL) { + ret = netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); + if (ret == NSERROR_OK) { nsoption_read(choices, nsoptions); free(choices); } diff --git a/gtk/scaffolding.c b/gtk/scaffolding.c index 436830bd3..fde195657 100644 --- a/gtk/scaffolding.c +++ b/gtk/scaffolding.c @@ -30,6 +30,7 @@ #include "utils/url.h" #include "utils/log.h" #include "utils/nsoption.h" +#include "utils/file.h" #include "desktop/browser_history.h" #include "desktop/browser_private.h" #include "desktop/hotlist.h" @@ -72,7 +73,6 @@ #include "gtk/tabs.h" #include "gtk/schedule.h" - /** Macro to define a handler for menu, button and activate events. */ #define MULTIHANDLER(q)\ static gboolean nsgtk_on_##q##_activate(struct gtk_scaffolding *g);\ @@ -867,7 +867,7 @@ MULTIHANDLER(print) GtkPrintSettings *print_settings; GtkPrintOperationResult res = GTK_PRINT_OPERATION_RESULT_ERROR; struct print_settings *nssettings; - char *settings_fname; + char *settings_fname = NULL; print_op = gtk_print_operation_new(); if (print_op == NULL) { @@ -876,7 +876,7 @@ MULTIHANDLER(print) } /* use previously saved settings if any */ - settings_fname = filepath_append(nsgtk_config_home, "Print"); + netsurf_mkpath(&settings_fname, NULL, 2, nsgtk_config_home, "Print"); if (settings_fname != NULL) { print_settings = gtk_print_settings_new_from_file(settings_fname, NULL); if (print_settings != NULL) { @@ -1260,7 +1260,7 @@ MULTIHANDLER(downloads) MULTIHANDLER(savewindowsize) { int x,y,w,h; - char *choices; + char *choices = NULL; gtk_window_get_position(g->window, &x, &y); gtk_window_get_size(g->window, &w, &h); @@ -1270,7 +1270,7 @@ MULTIHANDLER(savewindowsize) nsoption_set_int(window_x, x); nsoption_set_int(window_y, y); - choices = filepath_append(nsgtk_config_home, "Choices"); + netsurf_mkpath(&choices, NULL, 2, nsgtk_config_home, "Choices"); if (choices != NULL) { nsoption_write(choices, NULL, NULL); free(choices); diff --git a/monkey/fetch.c b/monkey/fetch.c index 668ad0e64..e2530e710 100644 --- a/monkey/fetch.c +++ b/monkey/fetch.c @@ -78,45 +78,6 @@ static char *url_to_path(const char *url) return respath; } -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ - -static char *filename_from_path(char *path) -{ - char *leafname; - - leafname = strrchr(path, '/'); - if (!leafname) - leafname = path; - else - leafname += 1; - - return strdup(leafname); -} - -/** - * Add a path component/filename to an existing path - * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success - */ - -static bool path_add_part(char *path, int length, const char *newpart) -{ - if(path[strlen(path) - 1] != '/') - strncat(path, "/", length); - - strncat(path, newpart, length); - - return true; -} - static nsurl *gui_get_resource_url(const char *path) { char buf[PATH_MAX]; @@ -133,8 +94,6 @@ static nsurl *gui_get_resource_url(const char *path) } static struct gui_fetch_table fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = monkey_fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, diff --git a/riscos/gui.c b/riscos/gui.c index db57ff68a..6447e4d90 100644 --- a/riscos/gui.c +++ b/riscos/gui.c @@ -67,6 +67,7 @@ #include "desktop/save_complete.h" #include "desktop/treeview.h" #include "render/font.h" +#include "utils/file.h" #include "riscos/content-handlers/artworks.h" #include "riscos/bitmap.h" @@ -2285,77 +2286,173 @@ void PDF_Password(char **owner_pass, char **user_pass, char *path) *owner_pass = NULL; } -/** - * Return the filename part of a full path - * - * \param path full path and filename - * \return filename (will be freed with free()) - */ -static char *filename_from_path(char *path) +#define DIR_SEP ('.') + +/** + * Generate a riscos path from one or more component elemnts. + * + * Constructs a complete path element from passed components. The + * second (and subsequent) components have a slash substituted for all + * riscos directory separators. + * + * If a string is allocated it must be freed by the caller. + * + * @param[in,out] str pointer to string pointer if this is NULL enough + * storage will be allocated for the complete path. + * @param[in,out] size The size of the space available if \a str not + * NULL on input and if not NULL set to the total + * output length on output. + * @param[in] nemb The number of elements. + * @param[in] ap The elements of the path as string pointers. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ +static nserror riscos_mkpath(char **str, size_t *size, size_t nelm, va_list ap) { - char *leafname; + const char *elm[16]; + size_t elm_len[16]; + size_t elm_idx; + char *fname; + size_t fname_len = 0; + char *curp; + size_t idx; + + /* check the parameters are all sensible */ + if ((nelm == 0) || (nelm > 16)) { + return NSERROR_BAD_PARAMETER; + } + if ((*str != NULL) && (size == NULL)) { + /* if the caller is providing the buffer they must say + * how much space is available. + */ + return NSERROR_BAD_PARAMETER; + } + + /* calculate how much storage we need for the complete path + * with all the elements. + */ + for (elm_idx = 0; elm_idx < nelm; elm_idx++) { + elm[elm_idx] = va_arg(ap, const char *); + elm_len[elm_idx] = strlen(elm[elm_idx]); + fname_len += elm_len[elm_idx]; + } + fname_len += nelm; /* allow for separators and terminator */ + + /* ensure there is enough space */ + fname = *str; + if (fname != NULL) { + if (fname_len > *size) { + return NSERROR_NOSPACE; + } + } else { + fname = malloc(fname_len); + if (fname == NULL) { + return NSERROR_NOMEM; + } + } + + /* copy the elements in with directory separator */ + curp = fname; + + /* first element is not altered */ + memmove(curp, elm[elm_idx], elm_len[elm_idx]); + curp += elm_len[elm_idx]; + /* ensure there is a delimiter */ + if (curp[-1] != DIR_SEP) { + *curp = DIR_SEP; + curp++; + } + + /* subsequent elemnts have slashes substituted with directory + * separators. + */ + for (elm_idx = 1; elm_idx < nelm; elm_idx++) { + for (idx = 0; idx < elm_len[elm_idx]; idx++) { + if (elm[elm_idx][idx] == DIR_SEP) { + *curp = '/'; + } else { + *curp = elm[elm_idx][idx]; + } + curp++; + } + *curp = DIR_SEP; + curp++; + } + curp[-1] = 0; /* NULL terminate */ + + assert((curp - fname) <= (int)fname_len); + + *str = fname; + if (size != NULL) { + *size = fname_len; + } + + return NSERROR_OK; + +} + + +/** + * Get the basename of a file using posix path handling. + * + * This gets the last element of a path and returns it. The returned + * element has all forward slashes translated into riscos directory + * separators. + * + * @param[in] path The path to extract the name from. + * @param[in,out] str Pointer to string pointer if this is NULL enough + * storage will be allocated for the path element. + * @param[in,out] size The size of the space available if \a + * str not NULL on input and set to the total + * output length on output. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ +static nserror riscos_basename(const char *path, char **str, size_t *size) +{ + const char *leafname; + char *fname; char *temp; - int leaflen; - temp = strrchr(path, '.'); - if (!temp) - temp = path; /* already leafname */ - else - temp += 1; + if (path == NULL) { + return NSERROR_BAD_PARAMETER; + } - leaflen = strlen(temp); - - leafname = malloc(leaflen + 1); + leafname = strrchr(path, DIR_SEP); if (!leafname) { - LOG(("malloc failed")); - return NULL; + leafname = path; + } else { + leafname += 1; } - memcpy(leafname, temp, leaflen + 1); + fname = strdup(leafname); + if (fname == NULL) { + return NSERROR_NOMEM; + } + + /** @todo check this leafname translation is actually required */ /* and s/\//\./g */ - for (temp = leafname; *temp; temp++) - if (*temp == '/') - *temp = '.'; - - return leafname; -} - -/** - * Add a path component/filename to an existing path - * - * \param path buffer containing platform-native format path + free space - * \param length length of buffer "path" - * \param newpart string containing unix-format path component to add to path - * \return true on success - */ - -static bool path_add_part(char *path, int length, const char *newpart) -{ - size_t path_len = strlen(path); - - /* Append directory separator, if there isn't one */ - if (path[path_len - 1] != '.') { - strncat(path, ".", length); - path_len += 1; + for (temp = fname; *temp != 0; temp++) { + if (*temp == '/') { + *temp = DIR_SEP; + } } - strncat(path, newpart, length); - - /* Newpart is either a directory name, or a file leafname - * Either way, we must replace all dots with forward slashes */ - for (path = path + path_len; *path; path++) { - if (*path == '.') - *path = '/'; + *str = fname; + if (size != NULL) { + *size = strlen(fname); } - - return true; + return NSERROR_OK; } +static struct gui_file_table riscos_file_table = { + .mkpath = riscos_mkpath, + .basename = riscos_basename, +}; + static struct gui_fetch_table riscos_fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, @@ -2392,6 +2489,7 @@ int main(int argc, char** argv) .clipboard = riscos_clipboard_table, .download = riscos_download_table, .fetch = &riscos_fetch_table, + .file = &riscos_file_table, .utf8 = riscos_utf8_table, .search = riscos_search_table, }; diff --git a/utils/Makefile b/utils/Makefile index aef579948..c808b91ba 100644 --- a/utils/Makefile +++ b/utils/Makefile @@ -2,6 +2,6 @@ S_UTILS := base64.c corestrings.c filename.c filepath.c hashtable.c \ libdom.c locale.c log.c messages.c nsurl.c talloc.c url.c \ - utf8.c utils.c useragent.c bloom.c nsoption.c + utf8.c utils.c useragent.c bloom.c nsoption.c file.c S_UTILS := $(addprefix utils/,$(S_UTILS)) diff --git a/utils/file.c b/utils/file.c new file mode 100644 index 000000000..7b0a4677a --- /dev/null +++ b/utils/file.c @@ -0,0 +1,117 @@ +/* + * Copyright 2014 vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * 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, see . + */ + +/** \file + * Table operations for files with posix compliant path separator. + */ + +#include +#include + +#include "desktop/gui_factory.h" +#include "utils/utils.h" + +#include "utils/file.h" + +/** + * Generate a posix path from one or more component elemnts. + * + * If a string is allocated it must be freed by the caller. + * + * @param[in,out] str pointer to string pointer if this is NULL enough + * storage will be allocated for the complete path. + * @param[in,out] size The size of the space available if \a str not + * NULL on input and if not NULL set to the total + * output length on output. + * @param[in] nemb The number of elements. + * @param[in] ... The elements of the path as string pointers. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ +static nserror posix_mkpath(char **str, size_t *size, size_t nelm, va_list ap) +{ + return vsnstrjoin(str, size, '/', nelm, ap); +} + +/** + * Get the basename of a file using posix path handling. + * + * This gets the last element of a path and returns it. + * + * @param[in] path The path to extract the name from. + * @param[in,out] str Pointer to string pointer if this is NULL enough + * storage will be allocated for the path element. + * @param[in,out] size The size of the space available if \a + * str not NULL on input and set to the total + * output length on output. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ +static nserror posix_basename(const char *path, char **str, size_t *size) +{ + const char *leafname; + char *fname; + + if (path == NULL) { + return NSERROR_BAD_PARAMETER; + } + + leafname = strrchr(path, '/'); + if (!leafname) { + leafname = path; + } else { + leafname += 1; + } + + fname = strdup(leafname); + if (fname == NULL) { + return NSERROR_NOMEM; + } + + *str = fname; + if (size != NULL) { + *size = strlen(fname); + } + return NSERROR_OK; +} + +/* exported interface documented in utils/file.h */ +nserror netsurf_mkpath(char **str, size_t *size, size_t nelm, ...) +{ + va_list ap; + nserror ret; + + va_start(ap, nelm); + if (guit != NULL) { + ret = guit->file->mkpath(str, size, nelm, ap); + } else { + /* default to posix */ + ret = vsnstrjoin(str, size, '/', nelm, ap); + } + va_end(ap); + + return ret; +} + +/* default to using the posix file handling */ +static struct gui_file_table file_table = { + .mkpath = posix_mkpath, + .basename = posix_basename, +}; + +struct gui_file_table *default_file_table = &file_table; diff --git a/utils/file.h b/utils/file.h new file mode 100644 index 000000000..f183337c5 --- /dev/null +++ b/utils/file.h @@ -0,0 +1,95 @@ +/* + * Copyright 2014 Vincent Sanders + * + * This file is part of NetSurf, http://www.netsurf-browser.org/ + * + * NetSurf is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * NetSurf is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * 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, see . + */ + +/** \file + * Default operations table for files. + */ + +#ifndef NETSURF_UTILS_FILE_H +#define NETSURF_UTILS_FILE_H + +#include + +/** + * function table for file and filename operations. + * + * function table implementing GUI interface to file and filename + * functionality appropriate for the OS. + */ +struct gui_file_table { + /* Mandantory entries */ + + /** + * Generate a path from one or more component elemnts. + * + * If a string is allocated it must be freed by the caller. + * + * @param[in,out] str pointer to string pointer if this is NULL enough + * storage will be allocated for the complete path. + * @param[in,out] size The size of the space available if \a str not + * NULL on input and if not NULL set to the total + * output length on output. + * @param[in] nemb The number of elements. + * @param[in] ... The elements of the path as string pointers. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ + nserror (*mkpath)(char **str, size_t *size, size_t nemb, va_list ap); + + /** + * Get the basename of a file. + * + * This gets the last element of a path and returns it. + * + * @param[in] path The path to extract the name from. + * @param[in,out] str Pointer to string pointer if this is NULL enough + * storage will be allocated for the path element. + * @param[in,out] size The size of the space available if \a + * str not NULL on input and set to the total + * output length on output. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ + nserror (*basename)(const char *path, char **str, size_t *size); +}; + +/** Default (posix) file operation table. */ +struct gui_file_table *default_file_table; + +/** + * Generate a path from one or more component elemnts. + * + * If a string is allocated it must be freed by the caller. + * + * @warning If this is called before the gui operation tables are + * initialised the behaviour defaults to posix paths. Ensure this is + * the required behaviour. + * + * @param[in,out] str pointer to string pointer if this is NULL enough + * storage will be allocated for the complete path. + * @param[in,out] size The size of the space available if \a str not + * NULL on input and if not NULL set to the total + * output length on output. + * @param[in] nemb The number of elements. + * @param[in] ... The elements of the path as string pointers. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. + */ +nserror netsurf_mkpath(char **str, size_t *size, size_t nelm, ...); + +#endif diff --git a/utils/filepath.c b/utils/filepath.c index 68e8d7e79..c28a821d7 100644 --- a/utils/filepath.c +++ b/utils/filepath.c @@ -325,25 +325,6 @@ 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) { diff --git a/utils/filepath.h b/utils/filepath.h index 56e9eff74..61b7209cc 100644 --- a/utils/filepath.h +++ b/utils/filepath.h @@ -127,17 +127,6 @@ char **filepath_path_to_strvec(const char *path); 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. * diff --git a/windows/gui.c b/windows/gui.c index 0af54ee62..0be75e1e8 100644 --- a/windows/gui.c +++ b/windows/gui.c @@ -43,6 +43,7 @@ #include "utils/log.h" #include "utils/messages.h" #include "utils/utils.h" +#include "utils/file.h" #include "windows/window.h" #include "windows/about.h" @@ -1808,42 +1809,75 @@ nsws_create_main_class(HINSTANCE hinstance) { } /** - * Return the filename part of a full path + * Generate a windows path from one or more component elemnts. * - * \param path full path and filename - * \return filename (will be freed with free()) + * If a string is allocated it must be freed by the caller. + * + * @param[in,out] str pointer to string pointer if this is NULL enough + * storage will be allocated for the complete path. + * @param[in,out] size The size of the space available if \a str not + * NULL on input and if not NULL set to the total + * output length on output. + * @param[in] nemb The number of elements. + * @param[in] ... The elements of the path as string pointers. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. */ -static char *filename_from_path(char *path) +static nserror windows_mkpath(char **str, size_t *size, size_t nelm, va_list ap) { - char *leafname; - - leafname = strrchr(path, '\\'); - if (!leafname) - leafname = path; - else - leafname += 1; - - return strdup(leafname); + return vsnstrjoin(str, size, '\\', nelm, ap); } /** - * Add a path component/filename to an existing path + * Get the basename of a file using windows path handling. * - * \param path buffer containing path + free space - * \param length length of buffer "path" - * \param newpart string containing path component to add to path - * \return true on success + * This gets the last element of a path and returns it. + * + * @param[in] path The path to extract the name from. + * @param[in,out] str Pointer to string pointer if this is NULL enough + * storage will be allocated for the path element. + * @param[in,out] size The size of the space available if \a + * str not NULL on input and set to the total + * output length on output. + * @return NSERROR_OK and the complete path is written to str + * or error code on faliure. */ -static bool path_add_part(char *path, int length, const char *newpart) +static nserror windows_basename(const char *path, char **str, size_t *size) { - if(path[strlen(path) - 1] != '\\') - strncat(path, "\\", length); + const char *leafname; + char *fname; - strncat(path, newpart, length); + if (path == NULL) { + return NSERROR_BAD_PARAMETER; + } - return true; + leafname = strrchr(path, '\\'); + if (!leafname) { + leafname = path; + } else { + leafname += 1; + } + + fname = strdup(leafname); + if (fname == NULL) { + return NSERROR_NOMEM; + } + + *str = fname; + if (size != NULL) { + *size = strlen(fname); + } + return NSERROR_OK; } +/* default to using the posix file handling */ +static struct gui_file_table file_table = { + .mkpath = windows_mkpath, + .basename = windows_basename, +}; + +struct gui_file_table *win32_file_table = &file_table; + static struct gui_window_table window_table = { .create = gui_window_create, .destroy = gui_window_destroy, @@ -1876,8 +1910,6 @@ struct gui_clipboard_table *win32_clipboard_table = &clipboard_table; static struct gui_fetch_table fetch_table = { - .filename_from_path = filename_from_path, - .path_add_part = path_add_part, .filetype = fetch_filetype, .path_to_url = path_to_url, .url_to_path = url_to_path, diff --git a/windows/gui.h b/windows/gui.h index d8e1b1d0b..82ae83642 100644 --- a/windows/gui.h +++ b/windows/gui.h @@ -24,6 +24,7 @@ #include "desktop/gui.h" #include "windows/localhistory.h" +struct gui_file_table *win32_file_table; extern struct gui_window_table *win32_window_table; extern struct gui_clipboard_table *win32_clipboard_table; extern struct gui_fetch_table *win32_fetch_table; diff --git a/windows/main.c b/windows/main.c index 19cd44a43..22cb5207c 100644 --- a/windows/main.c +++ b/windows/main.c @@ -111,6 +111,7 @@ WinMain(HINSTANCE hInstance, HINSTANCE hLastInstance, LPSTR lpcli, int ncmd) .clipboard = win32_clipboard_table, .download = win32_download_table, .fetch = win32_fetch_table, + .file = win32_file_table, .utf8 = win32_utf8_table, }; win32_fetch_table->get_resource_url = gui_get_resource_url;