From 0693c70f577624e66fd660a888f2d2bcd3fc180b Mon Sep 17 00:00:00 2001 From: Greg Ercolano Date: Fri, 10 Jul 2020 21:49:00 -0700 Subject: First pass at fixing issue 99 A lot of code touched because low level functions needed to pass up error messages reliably, and this had to propagate up the entire driver hierarchy. Tested OK *in English* on: > Linux > OSX 10.10.x > Windows VS2017 > Windows mingw64 I have no way to test on Android, but it might work. TODO: Needs testing in other languages to verify proper UTF8 error messages, esp. with Windows VS, due to complexities with FormatMessage() -- see get_ms_errmsg() --- src/Fl_File_Browser.cxx | 79 ++++++++++++----- src/Fl_File_Chooser.cxx | 23 +++++ src/Fl_File_Chooser.fl | 29 +++++-- src/Fl_File_Chooser2.cxx | 31 ++++--- src/Fl_System_Driver.H | 8 +- src/Fl_System_Driver.cxx | 6 +- src/drivers/Android/Fl_Android_System_Driver.H | 8 +- src/drivers/Android/Fl_Android_System_Driver.cxx | 12 ++- src/drivers/Darwin/Fl_Darwin_System_Driver.H | 4 +- src/drivers/Darwin/Fl_Darwin_System_Driver.cxx | 9 +- src/drivers/WinAPI/Fl_WinAPI_System_Driver.H | 8 +- src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx | 15 ++-- src/drivers/X11/Fl_X11_System_Driver.H | 4 +- src/drivers/X11/Fl_X11_System_Driver.cxx | 22 ++++- src/filename_isdir.cxx | 1 + src/filename_list.cxx | 3 +- src/scandir_posix.c | 103 ++++++++++++----------- src/scandir_win32.c | 101 ++++++++++++++++------ 18 files changed, 326 insertions(+), 140 deletions(-) (limited to 'src') diff --git a/src/Fl_File_Browser.cxx b/src/Fl_File_Browser.cxx index e7a7ea8d4..cb92428dd 100644 --- a/src/Fl_File_Browser.cxx +++ b/src/Fl_File_Browser.cxx @@ -354,10 +354,10 @@ Fl_File_Browser::item_draw(void *p, // I - List item data } -// -// 'Fl_File_Browser::Fl_File_Browser()' - Create a Fl_File_Browser widget. -// - +/** + The constructor creates the Fl_File_Browser widget at the specified position and size. + The destructor destroys the widget and frees all memory that has been allocated. +*/ Fl_File_Browser::Fl_File_Browser(int X, // I - Upper-lefthand X coordinate int Y, // I - Upper-lefthand Y coordinate int W, // I - Width in pixels @@ -370,13 +370,40 @@ Fl_File_Browser::Fl_File_Browser(int X, // I - Upper-lefthand X coordina directory_ = ""; iconsize_ = (uchar)(3 * textsize() / 2); filetype_ = FILES; + errmsg_ = NULL; } -// -// 'Fl_File_Browser::load()' - Load a directory into the browser. -// +// DTOR +Fl_File_Browser::~Fl_File_Browser() { + errmsg(NULL); // free()s prev errmsg, if any +} + + +/** + Sets OS error message to a string, which can be NULL. + Frees previous if any. + void errmsg(const char *emsg); + */ +void Fl_File_Browser::errmsg(const char* emsg) { + if ( errmsg_ ) { free((void*)errmsg_); errmsg_ = NULL; } + errmsg_ = emsg ? strdup(emsg) : NULL; +} + + +/** + Loads the specified directory into the browser. If icons have been + loaded then the correct icon is associated with each file in the list. + If directory is "", all mount points (unix) or drive letters (Windows) + are listed. + + The sort argument specifies a sort function to be used with + fl_filename_list(). + + Return value is the number of filename entries, or 0 if none. + On error, 0 is returned, and errmsg() has OS error string if non-NULL. +*/ int // O - Number of files loaded Fl_File_Browser::load(const char *directory,// I - Directory to load Fl_File_Sort_F *sort) // I - Sort function to use @@ -387,6 +414,7 @@ Fl_File_Browser::load(const char *directory,// I - Directory to load char filename[4096]; // Current file Fl_File_Icon *icon; // Icon to use + errmsg(NULL); // clear errors first // printf("Fl_File_Browser::load(\"%s\")\n", directory); @@ -394,11 +422,12 @@ Fl_File_Browser::load(const char *directory,// I - Directory to load directory_ = directory; - if (!directory) - return (0); + if (!directory) { + errmsg("NULL directory specified"); + return 0; + } - if (directory_[0] == '\0') - { + if (directory_[0] == '\0') { // // No directory specified; for UNIX list all mount points. For DOS // list all valid drive letters... @@ -406,21 +435,25 @@ Fl_File_Browser::load(const char *directory,// I - Directory to load if ((icon = Fl_File_Icon::find("any", Fl_File_Icon::DEVICE)) == NULL) icon = Fl_File_Icon::find("any", Fl_File_Icon::DIRECTORY); num_files = Fl::system_driver()->file_browser_load_filesystem(this, filename, (int)sizeof(filename), icon); - } - else - { - dirent **files; // Files in in directory - // - // Build the file list... - // - num_files = Fl::system_driver()->file_browser_load_directory(directory_, filename, sizeof(filename), &files, sort); - if (num_files <= 0) - return (0); + } else { + dirent **files; // Files in in directory + char emsg[1024] = ""; + + // Build the file list, check for errors + num_files = Fl::system_driver()->file_browser_load_directory(directory_, + filename, sizeof(filename), + &files, sort, + emsg, sizeof(emsg)); + // printf("Fl_File_Browser::load(dir='%s',filename='%s'): failed, emsg='%s'\n", directory_, filename, emsg); + + if (num_files <= 0) { + errmsg(emsg); + return 0; + } for (i = 0, num_dirs = 0; i < num_files; i ++) { if (strcmp(files[i]->d_name, "./")) { - snprintf(filename, sizeof(filename), "%s/%s", directory_, - files[i]->d_name); + fl_snprintf(filename, sizeof(filename), "%s/%s", directory_, files[i]->d_name); icon = Fl_File_Icon::find(filename); if ((icon && icon->type() == Fl_File_Icon::DIRECTORY) || diff --git a/src/Fl_File_Chooser.cxx b/src/Fl_File_Chooser.cxx index 8036ffaed..22fadf57f 100644 --- a/src/Fl_File_Chooser.cxx +++ b/src/Fl_File_Chooser.cxx @@ -197,9 +197,18 @@ Fl_File_Chooser::Fl_File_Chooser(const char *d, const char *p, int t, const char o->callback((Fl_Callback*)cb_); { fileList = new Fl_File_Browser(10, 45, 295, 225); fileList->type(2); + fileList->box(FL_DOWN_BOX); fileList->callback((Fl_Callback*)cb_fileList); fileList->window()->hotspot(fileList); } // Fl_File_Browser* fileList + { errorBox = new Fl_Box(10, 45, 295, 225, "dynamic error display"); + errorBox->box(FL_DOWN_BOX); + errorBox->color(FL_BACKGROUND2_COLOR); + errorBox->labelsize(18); + errorBox->labelcolor((Fl_Color)1); + errorBox->align(Fl_Align(133|FL_ALIGN_INSIDE)); + errorBox->hide(); + } // Fl_Box* errorBox { previewBox = new Fl_Box(305, 45, 175, 225, "?"); previewBox->box(FL_DOWN_BOX); previewBox->labelsize(100); @@ -471,3 +480,17 @@ Fl_Widget* Fl_File_Chooser::add_extra(Fl_Widget* gr) { } return ret; } + +/** + Show error box if val=1, hide if val=0 +*/ +void Fl_File_Chooser::show_error_box(int val) { + if ( val ) { + errorBox->color(fileList->color()); // inherit fileList's bg color + errorBox->show(); + fileList->hide(); + } else { + errorBox->hide(); + fileList->show(); + } +} diff --git a/src/Fl_File_Chooser.fl b/src/Fl_File_Chooser.fl index d222bd1c8..7cf01e1c7 100644 --- a/src/Fl_File_Chooser.fl +++ b/src/Fl_File_Chooser.fl @@ -66,7 +66,8 @@ class FL_EXPORT Fl_File_Chooser {open } decl {void update_preview();} {private local } - Function {Fl_File_Chooser(const char *d, const char *p, int t, const char *title)} {} { + Function {Fl_File_Chooser(const char *d, const char *p, int t, const char *title)} {open + } { code {if (!prefs_) { prefs_ = new Fl_Preferences(Fl_Preferences::CORE_USER, "fltk.org", "filechooser"); }} {} @@ -77,11 +78,11 @@ class FL_EXPORT Fl_File_Chooser {open fileList->deselect(); Fl::remove_timeout((Fl_Timeout_Handler)previewCB, this); window->hide();} open - private xywh {507 327 490 380} type Double hide resizable + private xywh {1171 438 490 380} type Double resizable code0 {if (title) window->label(title);} code1 {\#include } code2 {\#include } - code3 {\#include } modal + code3 {\#include } modal visible } { Fl_Group {} {open private xywh {10 10 470 25} @@ -106,14 +107,18 @@ window->hide();} open } } Fl_Tile {} { - callback {update_preview();} + callback {update_preview();} open private xywh {10 45 470 225} resizable } { Fl_File_Browser fileList { callback {fileListCB();} - private xywh {10 45 295 225} type Hold hotspot + private xywh {10 45 295 225} type Hold box DOWN_BOX hotspot code0 {\#include } } + Fl_Box errorBox { + label {dynamic error display} selected + private xywh {10 45 295 225} box DOWN_BOX color 7 labelsize 18 labelcolor 1 align 149 hide + } Fl_Box previewBox { label {?} private xywh {305 45 175 225} box DOWN_BOX labelsize 100 align 80 @@ -349,7 +354,7 @@ okButton->parent()->init_sizes();} {} } { code {fileList->textfont(f);} {} } - Function {textfont()} {selected return_type Fl_Font + Function {textfont()} {return_type Fl_Font } { code {return (fileList->textfont());} {} } @@ -475,6 +480,18 @@ window->resizable(svres);} {} } code {return ret;} {} } + Function {show_error_box(int val)} { + comment {Show error box if val=1, hide if val=0} open protected return_type void + } { + code {if ( val ) { + errorBox->color(fileList->color()); // inherit fileList's bg color + errorBox->show(); + fileList->hide(); +} else { + errorBox->hide(); + fileList->show(); +}} {} + } } decl {FL_EXPORT char *fl_dir_chooser(const char *message,const char *fname,int relative=0);} {public local diff --git a/src/Fl_File_Chooser2.cxx b/src/Fl_File_Chooser2.cxx index de2b7c160..ec04bd77c 100644 --- a/src/Fl_File_Chooser2.cxx +++ b/src/Fl_File_Chooser2.cxx @@ -1020,17 +1020,15 @@ void Fl_File_Chooser::preview(int e) Fl_Group *p = previewBox->parent(); if (e) { int w = p->w() * 2 / 3; - fileList->resize(fileList->x(), fileList->y(), - w, fileList->h()); - previewBox->resize(fileList->x()+w, previewBox->y(), - p->w()-w, previewBox->h()); + fileList->resize(fileList->x(), fileList->y(), w, fileList->h()); + errorBox->resize(errorBox->x(), errorBox->y(), w, errorBox->h()); + previewBox->resize(fileList->x()+w, previewBox->y(), p->w()-w, previewBox->h()); previewBox->show(); update_preview(); } else { - fileList->resize(fileList->x(), fileList->y(), - p->w(), fileList->h()); - previewBox->resize(p->x()+p->w(), previewBox->y(), - 0, previewBox->h()); + fileList->resize(fileList->x(), fileList->y(), p->w(), fileList->h()); + errorBox->resize(errorBox->x(), errorBox->y(), p->w(), errorBox->h()); + previewBox->resize(p->x()+p->w(), previewBox->y(), 0, previewBox->h()); previewBox->hide(); } p->init_sizes(); @@ -1070,7 +1068,14 @@ Fl_File_Chooser::rescan() okButton->deactivate(); // Build the file list... - fileList->load(directory_, sort); + if ( fileList->load(directory_, sort) <= 0 ) { + if ( fileList->errmsg() ) errorBox->label(fileList->errmsg()); // show OS errormsg when possible + else errorBox->label("No files found..."); + show_error_box(1); + } else { + show_error_box(0); + } + if (Fl::system_driver()->dot_file_hidden() && !showHiddenButton->value()) remove_hidden_files(); // Update the preview box... update_preview(); @@ -1094,7 +1099,13 @@ void Fl_File_Chooser::rescan_keep_filename() strlcpy(pathname, fn, sizeof(pathname)); // Build the file list... - fileList->load(directory_, sort); + if (fileList->load(directory_, sort) <= 0) { + if ( fileList->errmsg() ) errorBox->label(fileList->errmsg()); // show OS errormsg when possible + else errorBox->label("No files found..."); + show_error_box(1); + } else { + show_error_box(0); + } if (Fl::system_driver()->dot_file_hidden() && !showHiddenButton->value()) remove_hidden_files(); // Update the preview box... update_preview(); diff --git a/src/Fl_System_Driver.H b/src/Fl_System_Driver.H index 0474154d4..77a58971f 100644 --- a/src/Fl_System_Driver.H +++ b/src/Fl_System_Driver.H @@ -128,7 +128,9 @@ public: virtual int event_key(int k) {return 0;} virtual int get_key(int k) {return 0;} // implement scandir-like function - virtual int filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ) {return -1;} + virtual int filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg=NULL, int errmsg_sz=0) {return -1;} // the default implementation of filename_expand() may be enough virtual int filename_expand(char *to, int tolen, const char *from); // to implement @@ -162,7 +164,9 @@ public: // implement to support Fl_File_Browser::load() virtual int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon) {return 0;} // the default implementation of file_browser_load_directory() should be enough - virtual int file_browser_load_directory(const char *directory, char *filename, size_t name_size, dirent ***pfiles, Fl_File_Sort_F *sort); + virtual int file_browser_load_directory(const char *directory, char *filename, size_t name_size, + dirent ***pfiles, Fl_File_Sort_F *sort, + char *errmsg=NULL, int errmsg_sz=0); // implement to support Fl_Preferences virtual void newUUID(char *uuidBuffer) { uuidBuffer[0] = 0; } // implement to support Fl_Preferences diff --git a/src/Fl_System_Driver.cxx b/src/Fl_System_Driver.cxx index f2b3c9584..a68d3b381 100644 --- a/src/Fl_System_Driver.cxx +++ b/src/Fl_System_Driver.cxx @@ -449,9 +449,11 @@ int Fl_System_Driver::filename_expand(char *to,int tolen, const char *from) { } int Fl_System_Driver::file_browser_load_directory(const char *directory, char *filename, - size_t name_size, dirent ***pfiles, Fl_File_Sort_F *sort) + size_t name_size, dirent ***pfiles, + Fl_File_Sort_F *sort, + char *errmsg, int errmsg_sz) { - return filename_list(directory, pfiles, sort); + return filename_list(directory, pfiles, sort, errmsg, errmsg_sz); } int Fl_System_Driver::file_type(const char *filename) diff --git a/src/drivers/Android/Fl_Android_System_Driver.H b/src/drivers/Android/Fl_Android_System_Driver.H index 2a8682ddb..198d7a2b3 100644 --- a/src/drivers/Android/Fl_Android_System_Driver.H +++ b/src/drivers/Android/Fl_Android_System_Driver.H @@ -70,7 +70,9 @@ public: // these 2 are in Fl_get_key_win32.cxx virtual int event_key(int k); virtual int get_key(int k); - virtual int filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ); + virtual int filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz); virtual int filename_expand(char *to,int tolen, const char *from); virtual int filename_relative(char *to, int tolen, const char *from, const char *base); virtual int filename_absolute(char *to, int tolen, const char *from); @@ -80,7 +82,9 @@ public: virtual int open_uri(const char *uri, char *msg, int msglen); virtual int use_recent_tooltip_fix() {return 1;} virtual int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon); - virtual int file_browser_load_directory(const char *directory, char *filename, size_t name_size, dirent ***pfiles, Fl_File_Sort_F *sort); + virtual int file_browser_load_directory(const char *directory, char *filename, size_t name_size, + dirent ***pfiles, Fl_File_Sort_F *sort, + char *errmsg=NULL, int errmsg_sz=0); virtual void newUUID(char *uuidBuffer); virtual char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor, const char *application); diff --git a/src/drivers/Android/Fl_Android_System_Driver.cxx b/src/drivers/Android/Fl_Android_System_Driver.cxx index e6aa970e4..0fa9671c5 100644 --- a/src/drivers/Android/Fl_Android_System_Driver.cxx +++ b/src/drivers/Android/Fl_Android_System_Driver.cxx @@ -486,11 +486,13 @@ int Fl_WinAPI_System_Driver::clocale_printf(FILE *output, const char *format, va return retval; } -int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ) { +int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz ) { // For Windows we have a special scandir implementation that uses // the Win32 "wide" functions for lookup, avoiding the code page mess // entirely. It also fixes up the trailing '/'. - return fl_scandir(d, list, 0, sort); + return fl_scandir(d, list, 0, sort, errmsg, errmsg_sz); } int Fl_WinAPI_System_Driver::filename_expand(char *to, int tolen, const char *from) { @@ -747,7 +749,9 @@ int Fl_WinAPI_System_Driver::file_browser_load_filesystem(Fl_File_Browser *brows } int Fl_WinAPI_System_Driver::file_browser_load_directory(const char *directory, char *filename, - size_t name_size, dirent ***pfiles, Fl_File_Sort_F *sort) + size_t name_size, dirent ***pfiles, + Fl_File_Sort_F *sort, + char *errmsg, int errmsg_sz) { strlcpy(filename, directory, name_size); int i = (int) (strlen(filename) - 1); @@ -756,7 +760,7 @@ int Fl_WinAPI_System_Driver::file_browser_load_directory(const char *directory, filename[2] = '/'; else if (filename[i] != '/' && filename[i] != '\\') strlcat(filename, "/", name_size); - return filename_list(filename, pfiles, sort); + return filename_list(filename, pfiles, sort, errmsg, errmsg_sz); } void Fl_WinAPI_System_Driver::newUUID(char *uuidBuffer) diff --git a/src/drivers/Darwin/Fl_Darwin_System_Driver.H b/src/drivers/Darwin/Fl_Darwin_System_Driver.H index ee7ab30ea..548277901 100644 --- a/src/drivers/Darwin/Fl_Darwin_System_Driver.H +++ b/src/drivers/Darwin/Fl_Darwin_System_Driver.H @@ -52,7 +52,9 @@ public: // these 2 are in Fl_get_key_mac.cxx virtual int event_key(int k); virtual int get_key(int k); - virtual int filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ); + virtual int filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg=NULL, int errmsg_sz=0); virtual int open_uri(const char *uri, char *msg, int msglen); virtual int need_test_shortcut_extra() {return 1;} virtual int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon); diff --git a/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx b/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx index 51a416b87..0c1a9de9e 100644 --- a/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx +++ b/src/drivers/Darwin/Fl_Darwin_System_Driver.cxx @@ -15,6 +15,7 @@ // #include "Fl_Darwin_System_Driver.H" +#include #include #include #include @@ -139,7 +140,9 @@ void *Fl_Darwin_System_Driver::get_carbon_function(const char *function_name) { return (carbon ? dlsym(carbon, function_name) : NULL); } -int Fl_Darwin_System_Driver::filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ) { +int Fl_Darwin_System_Driver::filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz) { int dirlen; char *dirloc; // Assume that locale encoding is no less dense than UTF-8 @@ -150,6 +153,10 @@ int Fl_Darwin_System_Driver::filename_list(const char *d, dirent ***list, int (* # else int n = scandir(dirloc, list, 0, (int(*)(const void*,const void*))sort); # endif + if (n==-1) { + if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); + return -1; + } // convert every filename to UTF-8, and append a '/' to all // filenames that are directories int i; diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H index e6446923d..91b230d5f 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H +++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H @@ -68,7 +68,9 @@ public: // these 2 are in Fl_get_key_win32.cxx virtual int event_key(int k); virtual int get_key(int k); - virtual int filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ); + virtual int filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg=NULL, int errmsg_sz=0); virtual int filename_expand(char *to,int tolen, const char *from); virtual int filename_relative(char *to, int tolen, const char *from, const char *base); virtual int filename_absolute(char *to, int tolen, const char *from); @@ -78,7 +80,9 @@ public: virtual int open_uri(const char *uri, char *msg, int msglen); virtual int use_recent_tooltip_fix() {return 1;} virtual int file_browser_load_filesystem(Fl_File_Browser *browser, char *filename, int lname, Fl_File_Icon *icon); - virtual int file_browser_load_directory(const char *directory, char *filename, size_t name_size, dirent ***pfiles, Fl_File_Sort_F *sort); + virtual int file_browser_load_directory(const char *directory, char *filename, size_t name_size, + dirent ***pfiles, Fl_File_Sort_F *sort, + char *errmsg=NULL, int errmsg_sz=0); virtual void newUUID(char *uuidBuffer); virtual char *preference_rootnode(Fl_Preferences *prefs, Fl_Preferences::Root root, const char *vendor, const char *application); diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx index ab20cfe32..592e192e3 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx +++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx @@ -61,7 +61,8 @@ static wchar_t *wbuf1 = NULL; extern "C" { int fl_scandir(const char *dirname, struct dirent ***namelist, int (*select)(struct dirent *), - int (*compar)(struct dirent **, struct dirent **)); + int (*compar)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_len); } /* @@ -488,11 +489,13 @@ int Fl_WinAPI_System_Driver::clocale_printf(FILE *output, const char *format, va return retval; } -int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ) { +int Fl_WinAPI_System_Driver::filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz) { // For Windows we have a special scandir implementation that uses // the Win32 "wide" functions for lookup, avoiding the code page mess // entirely. It also fixes up the trailing '/'. - return fl_scandir(d, list, 0, sort); + return fl_scandir(d, list, 0, sort, errmsg, errmsg_sz); } int Fl_WinAPI_System_Driver::filename_expand(char *to, int tolen, const char *from) { @@ -749,7 +752,9 @@ int Fl_WinAPI_System_Driver::file_browser_load_filesystem(Fl_File_Browser *brows } int Fl_WinAPI_System_Driver::file_browser_load_directory(const char *directory, char *filename, - size_t name_size, dirent ***pfiles, Fl_File_Sort_F *sort) + size_t name_size, dirent ***pfiles, + Fl_File_Sort_F *sort, + char *errmsg, int errmsg_sz) { strlcpy(filename, directory, name_size); int i = (int) (strlen(filename) - 1); @@ -758,7 +763,7 @@ int Fl_WinAPI_System_Driver::file_browser_load_directory(const char *directory, filename[2] = '/'; else if (filename[i] != '/' && filename[i] != '\\') strlcat(filename, "/", name_size); - return filename_list(filename, pfiles, sort); + return filename_list(filename, pfiles, sort, errmsg, errmsg_sz); } void Fl_WinAPI_System_Driver::newUUID(char *uuidBuffer) diff --git a/src/drivers/X11/Fl_X11_System_Driver.H b/src/drivers/X11/Fl_X11_System_Driver.H index 7c2306a28..f8fddc741 100644 --- a/src/drivers/X11/Fl_X11_System_Driver.H +++ b/src/drivers/X11/Fl_X11_System_Driver.H @@ -34,7 +34,9 @@ public: // these 2 are in Fl_get_key.cxx virtual int event_key(int k); virtual int get_key(int k); - virtual int filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ); + virtual int filename_list(const char *d, dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg=NULL, int errmsg_sz=0); virtual int need_menu_handle_part1_extra() {return 1;} virtual int open_uri(const char *uri, char *msg, int msglen); virtual int use_tooltip_timeout_condition() {return 1;} diff --git a/src/drivers/X11/Fl_X11_System_Driver.cxx b/src/drivers/X11/Fl_X11_System_Driver.cxx index 0714ff753..28827bcfc 100644 --- a/src/drivers/X11/Fl_X11_System_Driver.cxx +++ b/src/drivers/X11/Fl_X11_System_Driver.cxx @@ -25,7 +25,8 @@ #include #include #include - +#include // strerror(errno) +#include // errno #if defined(_AIX) extern "C" { @@ -56,7 +57,8 @@ extern "C" { extern "C" { int fl_scandir(const char *dirname, struct dirent ***namelist, int (*select)(struct dirent *), - int (*compar)(struct dirent **, struct dirent **)); + int (*compar)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz); } #endif @@ -444,10 +446,19 @@ int Fl_X11_System_Driver::XParseGeometry(const char* string, int* x, int* y, return ::XParseGeometry(string, x, y, width, height); } -int Fl_X11_System_Driver::filename_list(const char *d, dirent ***list, int (*sort)(struct dirent **, struct dirent **) ) { +// +// Needs some docs +// Returns -1 on error, errmsg will contain OS error if non-NULL. +// +int Fl_X11_System_Driver::filename_list(const char *d, + dirent ***list, + int (*sort)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz) { int dirlen; char *dirloc; + if (errmsg && errmsg_sz>0) errmsg[0] = '\0'; + // Assume that locale encoding is no less dense than UTF-8 dirlen = strlen(d); dirloc = (char *)malloc(dirlen + 1); @@ -476,6 +487,11 @@ int Fl_X11_System_Driver::filename_list(const char *d, dirent ***list, int (*sor free(dirloc); + if (n==-1) { + if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); + return -1; + } + // convert every filename to UTF-8, and append a '/' to all // filenames that are directories int i; diff --git a/src/filename_isdir.cxx b/src/filename_isdir.cxx index fad66b3c7..47e1de58f 100644 --- a/src/filename_isdir.cxx +++ b/src/filename_isdir.cxx @@ -57,6 +57,7 @@ int Fl_System_Driver::filename_isdir_quick(const char* n) { } +// TODO: This should probably handle errors better (like permission denied) -erco int Fl_System_Driver::filename_isdir(const char* n) { struct stat s; char fn[FL_PATH_MAX]; diff --git a/src/filename_list.cxx b/src/filename_list.cxx index 2ad240996..4c8204fe7 100644 --- a/src/filename_list.cxx +++ b/src/filename_list.cxx @@ -63,9 +63,10 @@ int fl_casealphasort(struct dirent **a, struct dirent **b) { to put unpadded numbers in consecutive order; upper and lowercase letters are compared according to their ASCII ordering - uppercase before lowercase. \return the number of entries if no error, a negative value otherwise. + \todo should support returning OS error messages */ int fl_filename_list(const char *d, dirent ***list, Fl_File_Sort_F *sort) { - return Fl::system_driver()->filename_list(d, list, sort); + return Fl::system_driver()->filename_list(d, list, sort, NULL, 0); } /** diff --git a/src/scandir_posix.c b/src/scandir_posix.c index cf58c4195..b311c72e1 100644 --- a/src/scandir_posix.c +++ b/src/scandir_posix.c @@ -128,11 +128,16 @@ readentry(DIR *dirp, struct dirent **entryp, size_t *len) } -/* ========================================================================== */ +/* + * This could use some docs. + * + * Returns -1 on error, errmsg returns error string (if non-NULL) + */ int fl_scandir(const char *dir, struct dirent ***namelist, int (*sel)(struct dirent *), - int (*compar)(struct dirent **, struct dirent **)) + int (*compar)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz) { { int result = -1; DIR *dirp; @@ -140,64 +145,62 @@ fl_scandir(const char *dir, struct dirent ***namelist, struct dirent *entryp, **entries, **p; entries = (struct dirent **) malloc(sizeof(*entries) * max); - if (NULL != entries) + if (NULL == entries) { + if (errmsg) fl_snprintf(errmsg, errmsg_sz, "out of memory"); + return -1; + } + + /* Open directory 'dir' (and verify that it really is a directory) */ + dirp = opendir(dir); + if (NULL == dirp) { + if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); + return -1; + } + + /* Read next directory entry */ + while (!readentry(dirp, &entryp, &len)) { - /* Open directory 'dir' (and verify that it really is a directory) */ - dirp = opendir(dir); - if (NULL != dirp) + if (NULL == entryp) { - /* Read next directory entry */ - while (!readentry(dirp, &entryp, &len)) - { - if (NULL == entryp) - { - /* EOD => Return number of directory entries */ - result = (int) num; - break; - } - /* Apply select function if there is one provided */ - if (NULL != sel) { if (!sel(entryp)) continue; } - entries[num++] = entryp; - if (num >= max) - { - /* Allocate exponentially increasing sized memory chunks */ - if (INT_MAX / 2 >= (int) max) { max *= (size_t) 2; } - else - { - errno = ENOMEM; - break; - } - p = (struct dirent **) realloc((void *) entries, - sizeof(*entries) * max); - if (NULL != p) { entries = p; } - else break; - } - } - closedir(dirp); - /* - * A standard compliant 'closedir()' is allowed to fail with 'EINTR', - * but the state of the directory structure is undefined in this case. - * Therefore we ignore the return value because we can't call 'closedir()' - * again and must hope that the system has released all resources. - */ + /* EOD => Return number of directory entries */ + result = (int) num; + break; } - /* Sort entries in array if there is a compare function provided */ - if (NULL != compar) - { - qsort((void *) entries, num, sizeof(*entries), - (int (*)(const void *, const void *)) compar); + /* Apply select function if there is one provided */ + if (NULL != sel) { if (!sel(entryp)) continue; } + entries[num++] = entryp; + if (num >= max) { + /* Allocate exponentially increasing sized memory chunks */ + if (INT_MAX / 2 >= (int) max) { max *= (size_t) 2; } + else { + errno = ENOMEM; + break; + } + p = (struct dirent **) realloc((void *)entries, sizeof(*entries)*max); + if (NULL != p) { entries = p; } + else break; } - *namelist = entries; } - + closedir(dirp); + /* + * A standard compliant 'closedir()' is allowed to fail with 'EINTR', + * but the state of the directory structure is undefined in this case. + * Therefore we ignore the return value because we can't call 'closedir()' + * again and must hope that the system has released all resources. + */ + + /* Sort entries in array if there is a compare function provided */ + if (NULL != compar) { + qsort((void *) entries, num, sizeof(*entries), + (int (*)(const void *, const void *)) compar); + } + *namelist = entries; /* Check for error */ - if (-1 == result) - { + if (-1 == result) { /* Free all memory we have allocated */ while (num--) { free(entries[num]); } free(entries); } - return result; } diff --git a/src/scandir_win32.c b/src/scandir_win32.c index 2663e30dc..822770234 100644 --- a/src/scandir_win32.c +++ b/src/scandir_win32.c @@ -15,16 +15,59 @@ */ #ifndef __CYGWIN__ -/* Emulation of POSIX scandir() call */ +/* Emulation of POSIX scandir() call with error messages */ #include #include #include "flstring.h" #include #include +/* Get error message string for last failed WIN32 operation + * in 'errmsg' (if non-NULL), string size limited to errmsg_sz. + * + * NOTE: Copied from: fluid/ExternalCodeEditor_WIN32.cxx + * + * TODO: Verify works in different languages, with utf8 strings. + * TODO: This should be made available globally to the FLTK internals, in case + * other parts of FLTK need OS error messages.. + */ +static void get_ms_errmsg(char *errmsg, int errmsg_sz) { + DWORD lastErr = GetLastError(); + DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM; + DWORD langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); + LPSTR mbuf = 0; + + // Early exit if parent doesn't want an errmsg + if (!errmsg || errmsg_sz<=0 ) return; + // Get error message from Windows + DWORD size = FormatMessageA(flags, 0, lastErr, langid, (LPSTR)&mbuf, 0, NULL); + if ( size == 0 ) { + fl_snprintf(errmsg, errmsg_sz, "Error #%lu", (unsigned long)lastErr); + } else { + int cnt = 0; + /* Copy mbuf -> errmsg, remove '\r's -- they screw up fl_alert()) */ + for ( char *src=mbuf, *dst=errmsg; 1; src++ ) { + if ( *src == '\0' ) { *dst = '\0'; break; } + if ( *src != '\r' ) { + if ( ++cnt >= errmsg_sz ) { *dst = '\0'; break; } // trunc on overflow + *dst++ = *src; + } + } + LocalFree(mbuf); /* Free the buffer allocated by the system */ + } +} + +/* + * This could use some docs. + * + * Returns -1 on error, errmsg returns error string (if non-NULL) + */ int fl_scandir(const char *dirname, struct dirent ***namelist, int (*select)(struct dirent *), - int (*compar)(struct dirent **, struct dirent **)) { + int (*compar)(struct dirent **, struct dirent **), + char *errmsg, int errmsg_sz) { int len; char *findIn, *d, is_dir = 0; WIN32_FIND_DATAW findw; @@ -33,9 +76,14 @@ int fl_scandir(const char *dirname, struct dirent ***namelist, struct dirent **dir = 0, *selectDir; unsigned long ret; + if (errmsg && errmsg_sz>0) errmsg[0] = '\0'; len = (int) strlen(dirname); findIn = (char *)malloc((size_t)(len+10)); - if (!findIn) return -1; + if (!findIn) { + /* win32 malloc() docs: "malloc sets errno to ENOMEM if allocation fails" */ + if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); + return -1; + } strcpy(findIn, dirname); /* #if defined(__GNUC__) */ @@ -49,7 +97,7 @@ int fl_scandir(const char *dirname, struct dirent ***namelist, if ((len>1) && (d[-1]=='.') && (d[-2]=='\\')) { d[-1] = '*'; is_dir = 1; } if (!is_dir) { /* this file may still be a directory that we need to list */ DWORD attr = GetFileAttributes(findIn); - if (attr&FILE_ATTRIBUTE_DIRECTORY) + if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY) ) strcpy(d, "\\*"); } { /* Create a block to limit the scope while we find the initial "wide" filename */ @@ -64,55 +112,54 @@ int fl_scandir(const char *dirname, struct dirent ***namelist, h = FindFirstFileW(wbuf, &findw); /* get a handle to the first filename in the search */ free(wbuf); /* release the "wide" buffer before the pointer goes out of scope */ } + if (h==INVALID_HANDLE_VALUE) { free(findIn); ret = GetLastError(); if (ret != ERROR_NO_MORE_FILES) { nDir = -1; + get_ms_errmsg(errmsg, errmsg_sz); /* return OS error msg */ } *namelist = dir; return nDir; } do { - int l = (int) wcslen(findw.cFileName); - int dstlen = l * 5 + 1; - selectDir=(struct dirent*)malloc(sizeof(struct dirent)+dstlen); + int l = (int) wcslen(findw.cFileName); + int dstlen = l * 5 + 1; + selectDir=(struct dirent*)malloc(sizeof(struct dirent)+dstlen); - /* l = fl_unicode2utf(findw.cFileName, l, selectDir->d_name); */ - l = fl_utf8fromwc(selectDir->d_name, dstlen, findw.cFileName, l); + /* l = fl_unicode2utf(findw.cFileName, l, selectDir->d_name); */ + l = fl_utf8fromwc(selectDir->d_name, dstlen, findw.cFileName, l); - selectDir->d_name[l] = 0; - if (findw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - /* Append a trailing slash to directory names... */ - strcat(selectDir->d_name, "/"); - } - if (!select || (*select)(selectDir)) { - if (nDir==NDir) { + selectDir->d_name[l] = 0; + if (findw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + /* Append a trailing slash to directory names... */ + strcat(selectDir->d_name, "/"); + } + if (!select || (*select)(selectDir)) { + if (nDir==NDir) { struct dirent **tempDir = (struct dirent **)calloc(sizeof(struct dirent*), (size_t)(NDir+33)); if (NDir) memcpy(tempDir, dir, sizeof(struct dirent*)*NDir); if (dir) free(dir); dir = tempDir; NDir += 32; - } - dir[nDir] = selectDir; - nDir++; - dir[nDir] = 0; - } else { - free(selectDir); - } - } while (FindNextFileW(h, &findw)); + } + dir[nDir] = selectDir; + nDir++; + dir[nDir] = 0; + } else { + free(selectDir); + } + } while (FindNextFileW(h, &findw)); ret = GetLastError(); if (ret != ERROR_NO_MORE_FILES) { /* don't return an error code, because the dir list may still be valid up to this point */ } FindClose(h); - free (findIn); - if (compar) qsort(dir, (size_t)nDir, sizeof(*dir), (int(*)(const void*, const void*))compar); - *namelist = dir; return nDir; } -- cgit v1.2.3 From 4471166f59499e011477a68c65141adf802d8cf4 Mon Sep 17 00:00:00 2001 From: Greg Ercolano Date: Sat, 11 Jul 2020 14:35:59 -0700 Subject: Extra fool-proofing, and suggested use of strerror_r() strerror_r() sounds like the better thing to use, but if you read the man page carefully, there are pitfalls of competing GNU vs POSIX versions of the same function, with different return values. GNU version returns a pointer to the error string, which may or may not use the error string supplied to use. Makes no sense. Why not use the buffer the user prepared? Makes returning errors hard. One thing GNU does underline is the string is always NUL terminated. POSIX version doesn't return a string at all, it returns an int, making handling the GNU cases impossible. POSIX /doesn't/ say one way or the other about the string being NUL terminated, so you have to make sure when you use it. I trawled the net, seems this is a big annoying issue. For now, leaving the code with strerror(), but we should change it. --- src/scandir_posix.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'src') diff --git a/src/scandir_posix.c b/src/scandir_posix.c index b311c72e1..7295d7e7d 100644 --- a/src/scandir_posix.c +++ b/src/scandir_posix.c @@ -144,6 +144,7 @@ fl_scandir(const char *dir, struct dirent ***namelist, size_t len, num = 0, max = ENTRIES_MIN; struct dirent *entryp, **entries, **p; + if (errmsg && errmsg_sz>0) errmsg[0] = '\0'; entries = (struct dirent **) malloc(sizeof(*entries) * max); if (NULL == entries) { if (errmsg) fl_snprintf(errmsg, errmsg_sz, "out of memory"); @@ -154,6 +155,17 @@ fl_scandir(const char *dir, struct dirent ***namelist, dirp = opendir(dir); if (NULL == dirp) { if (errmsg) fl_snprintf(errmsg, errmsg_sz, "%s", strerror(errno)); + + // XXX: This would be a thread safe alternative to the above, but commented + // out because we can get either GNU or POSIX versions on linux, + // which AFAICT are incompatible: GNU doesn't guarantee errmsg is used + // at all, whereas POSIX /only/ fills buffer. The two calls are not really + // compatible but have the same name and different return values.. wtf? + // + // if (errmsg && errmsg_sz > 0) { + // strerror_r(errno, errmsg, errmsg_sz); // thread safe. Might be GNU, might be POSIX + // errmsg[errmsg_sz-1] = '\0'; // force null term b/c XSI does not specify + // } return -1; } -- cgit v1.2.3 From 845224f4756239963eeea896fe68539c95d71a6f Mon Sep 17 00:00:00 2001 From: Albrecht Schlosser Date: Sun, 12 Jul 2020 21:14:16 +0200 Subject: Windows: convert error message to UTF-8 We need to get the error message in "wide character" format and convert it to UTF-8. The conversion would truncate it if the buffer was too small, hence we don't need to check for overflow. Tested on Windows 10 with VS 2010, including (faked) error codes with non-ASCII characters (German). --- src/scandir_win32.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/scandir_win32.c b/src/scandir_win32.c index 822770234..2210f7b49 100644 --- a/src/scandir_win32.c +++ b/src/scandir_win32.c @@ -1,7 +1,7 @@ /* * Windows scandir function for the Fast Light Tool Kit (FLTK). * - * Copyright 1998-2018 by Bill Spitzak and others. + * Copyright 1998-2020 by Bill Spitzak and others. * * This library is free software. Distribution and use rights are outlined in * the file "COPYING" which should have been included with this file. If this @@ -37,23 +37,23 @@ static void get_ms_errmsg(char *errmsg, int errmsg_sz) { FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM; DWORD langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); - LPSTR mbuf = 0; + LPWSTR mbuf = 0; + DWORD msize = 0; - // Early exit if parent doesn't want an errmsg - if (!errmsg || errmsg_sz<=0 ) return; - // Get error message from Windows - DWORD size = FormatMessageA(flags, 0, lastErr, langid, (LPSTR)&mbuf, 0, NULL); - if ( size == 0 ) { + /* Early exit if parent doesn't want an errmsg */ + if (!errmsg || errmsg_sz <= 0 ) return; + /* Get error message from Windows */ + msize = FormatMessageW(flags, 0, lastErr, langid, (LPWSTR)&mbuf, 0, NULL); + if ( msize == 0 ) { fl_snprintf(errmsg, errmsg_sz, "Error #%lu", (unsigned long)lastErr); } else { - int cnt = 0; - /* Copy mbuf -> errmsg, remove '\r's -- they screw up fl_alert()) */ - for ( char *src=mbuf, *dst=errmsg; 1; src++ ) { + /* convert message to UTF-8 */ + int mlen = fl_utf8fromwc(errmsg, errmsg_sz, mbuf, msize); + /* Remove '\r's -- they screw up fl_alert()) */ + char *src = errmsg, *dst = errmsg; + for ( ; 1; src++ ) { if ( *src == '\0' ) { *dst = '\0'; break; } - if ( *src != '\r' ) { - if ( ++cnt >= errmsg_sz ) { *dst = '\0'; break; } // trunc on overflow - *dst++ = *src; - } + if ( *src != '\r' ) { *dst++ = *src; } } LocalFree(mbuf); /* Free the buffer allocated by the system */ } -- cgit v1.2.3