diff options
| author | Greg Ercolano <erco@seriss.com> | 2020-07-10 21:49:00 -0700 |
|---|---|---|
| committer | Albrecht Schlosser <albrechts.fltk@online.de> | 2020-07-14 12:52:56 +0200 |
| commit | 0693c70f577624e66fd660a888f2d2bcd3fc180b (patch) | |
| tree | e3b4161c0516ebacaf84e9b58852f48d4e4b6357 | |
| parent | 9925b0f12852dc37fb7fb773a525486c258d2c62 (diff) | |
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()
| -rw-r--r-- | FL/Fl_File_Browser.H | 20 | ||||
| -rw-r--r-- | FL/Fl_File_Chooser.H | 3 | ||||
| -rw-r--r-- | src/Fl_File_Browser.cxx | 79 | ||||
| -rw-r--r-- | src/Fl_File_Chooser.cxx | 23 | ||||
| -rw-r--r-- | src/Fl_File_Chooser.fl | 29 | ||||
| -rw-r--r-- | src/Fl_File_Chooser2.cxx | 31 | ||||
| -rw-r--r-- | src/Fl_System_Driver.H | 8 | ||||
| -rw-r--r-- | src/Fl_System_Driver.cxx | 6 | ||||
| -rw-r--r-- | src/drivers/Android/Fl_Android_System_Driver.H | 8 | ||||
| -rw-r--r-- | src/drivers/Android/Fl_Android_System_Driver.cxx | 12 | ||||
| -rw-r--r-- | src/drivers/Darwin/Fl_Darwin_System_Driver.H | 4 | ||||
| -rw-r--r-- | src/drivers/Darwin/Fl_Darwin_System_Driver.cxx | 9 | ||||
| -rw-r--r-- | src/drivers/WinAPI/Fl_WinAPI_System_Driver.H | 8 | ||||
| -rw-r--r-- | src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx | 15 | ||||
| -rw-r--r-- | src/drivers/X11/Fl_X11_System_Driver.H | 4 | ||||
| -rw-r--r-- | src/drivers/X11/Fl_X11_System_Driver.cxx | 22 | ||||
| -rw-r--r-- | src/filename_isdir.cxx | 1 | ||||
| -rw-r--r-- | src/filename_list.cxx | 3 | ||||
| -rw-r--r-- | src/scandir_posix.c | 103 | ||||
| -rw-r--r-- | src/scandir_win32.c | 101 |
20 files changed, 336 insertions, 153 deletions
diff --git a/FL/Fl_File_Browser.H b/FL/Fl_File_Browser.H index 67b0a9d81..40d328093 100644 --- a/FL/Fl_File_Browser.H +++ b/FL/Fl_File_Browser.H @@ -40,6 +40,7 @@ class FL_EXPORT Fl_File_Browser : public Fl_Browser { const char *directory_; uchar iconsize_; const char *pattern_; + const char *errmsg_; int full_height() const; int item_height(void *) const; @@ -50,11 +51,8 @@ class FL_EXPORT Fl_File_Browser : public Fl_Browser { public: enum { FILES, DIRECTORIES }; - /** - 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(int, int, int, int, const char * = 0); + ~Fl_File_Browser(); /** Sets or gets the size of the icons. The default size is 20 pixels. */ uchar iconsize() const { return (iconsize_); }; @@ -73,16 +71,7 @@ public: function in FLTK. */ const char *filter() const { return (pattern_); }; - - /** - Loads the specified directory into the browser. If icons have been - loaded then the correct icon is associated with each file in the list. - - <P>The sort argument specifies a sort function to be used with - fl_filename_list(). - */ int load(const char *directory, Fl_File_Sort_F *sort = fl_numericsort); - Fl_Fontsize textsize() const { return Fl_Browser::textsize(); }; void textsize(Fl_Fontsize s) { Fl_Browser::textsize(s); iconsize_ = (uchar)(3 * s / 2); }; @@ -100,6 +89,11 @@ public: shown. */ void filetype(int t) { filetype_ = t; }; + void errmsg(const char *emsg); + /** + Returns OS error messages, or NULL if none. Use when advised. + */ + const char* errmsg() const { return errmsg_; } }; #endif // !_Fl_File_Browser_H_ diff --git a/FL/Fl_File_Chooser.H b/FL/Fl_File_Chooser.H index 702c4ff9f..341b8e683 100644 --- a/FL/Fl_File_Chooser.H +++ b/FL/Fl_File_Chooser.H @@ -85,6 +85,7 @@ private: Fl_File_Browser *fileList; inline void cb_fileList_i(Fl_File_Browser*, void*); static void cb_fileList(Fl_File_Browser*, void*); + Fl_Box *errorBox; Fl_Box *previewBox; public: Fl_Check_Button *previewButton; @@ -231,6 +232,8 @@ private: Fl_Widget* ext_group; public: Fl_Widget* add_extra(Fl_Widget* gr); +protected: + void show_error_box(int val); }; FL_EXPORT char *fl_dir_chooser(const char *message,const char *fname,int relative=0); FL_EXPORT char *fl_file_chooser(const char *message,const char *pat,const char *fname,int relative=0); 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 <stdio.h>} code2 {\#include <stdlib.h>} - code3 {\#include <string.h>} modal + code3 {\#include <string.h>} 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/Fl_File_Browser.H>} } + 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 <src/flstring.h> #include <FL/platform.H> #include <FL/Fl.H> #include <FL/Fl_File_Browser.H> @@ -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 <unistd.h> #include <sys/types.h> #include <pwd.h> - +#include <string.h> // strerror(errno) +#include <errno.h> // 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 <FL/platform_types.h> #include <FL/fl_utf8.h> #include "flstring.h" #include <windows.h> #include <stdlib.h> +/* 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; } |
