diff --git a/FL/Fl_System_Driver.H b/FL/Fl_System_Driver.H index e49306a96..5bcd8e994 100644 --- a/FL/Fl_System_Driver.H +++ b/FL/Fl_System_Driver.H @@ -63,7 +63,7 @@ public: static const int fl_YValue; static const int fl_XNegative; static const int fl_YNegative; - + // implement if the system adds unwanted program argument(s) virtual int single_arg(const char *arg) { return 0; } // implement if the system adds unwanted program argument pair(s) @@ -81,7 +81,7 @@ public: static void fatal(const char* format, ...); // implement to set the default effect of Fl::error() virtual void fatal(const char* format, va_list args); - + // implement these to support cross-platform file operations virtual char *utf2mbcs(const char *s) {return (char*)s;} virtual char *getenv(const char* v) {return NULL;} @@ -100,12 +100,13 @@ public: virtual int access(const char* f, int mode) { return -1;} virtual int stat(const char* f, struct stat *b) { return -1;} virtual char *getcwd(char* b, int l) {return NULL;} + virtual int chdir(const char* path) {return -1;} virtual int unlink(const char* fname) {return -1;} virtual int mkdir(const char* f, int mode) {return -1;} virtual int rmdir(const char* f) {return -1;} virtual int rename(const char* f, const char *n) {return -1;} - - // the default implementation of these utf8... functions should be enough + + // the default implementation of these utf8... functions should be enough virtual unsigned utf8towc(const char* src, unsigned srclen, wchar_t* dst, unsigned dstlen); virtual unsigned utf8fromwc(char* dst, unsigned dstlen, const wchar_t* src, unsigned srclen); virtual int utf8locale() {return 1;} diff --git a/FL/fl_utf8.h b/FL/fl_utf8.h index 68ebf3f05..f855f977f 100644 --- a/FL/fl_utf8.h +++ b/FL/fl_utf8.h @@ -160,10 +160,13 @@ FL_EXPORT int fl_chmod(const char* f, int mode); FL_EXPORT int fl_access(const char* f, int mode); /* OD: Portable UTF-8 aware stat wrapper */ -FL_EXPORT int fl_stat( const char *path, struct stat *buffer ); +FL_EXPORT int fl_stat(const char *path, struct stat *buffer); /* OD: Portable UTF-8 aware getcwd wrapper */ -FL_EXPORT char* fl_getcwd( char *buf, int maxlen); +FL_EXPORT char *fl_getcwd(char *buf, int len); + +/* Portable UTF-8 aware chdir wrapper */ +FL_EXPORT int fl_chdir(const char *path); /* OD: Portable UTF-8 aware fopen wrapper */ FL_EXPORT FILE *fl_fopen(const char *f, const char *mode); @@ -175,9 +178,9 @@ FL_EXPORT int fl_system(const char* f); FL_EXPORT int fl_execvp(const char *file, char *const *argv); /* OD: Portable UTF-8 aware open wrapper */ -FL_EXPORT int fl_open(const char* f, int o, ...); +FL_EXPORT int fl_open(const char *fname, int oflags, ...); -FL_EXPORT int fl_open_ext(const char* fname, int binary, int oflags, ...); +FL_EXPORT int fl_open_ext(const char *fname, int binary, int oflags, ...); /* OD: Portable UTF-8 aware unlink wrapper */ FL_EXPORT int fl_unlink(const char *fname); diff --git a/src/drivers/Posix/Fl_Posix_System_Driver.H b/src/drivers/Posix/Fl_Posix_System_Driver.H index 5b2060283..6b6d25f61 100644 --- a/src/drivers/Posix/Fl_Posix_System_Driver.H +++ b/src/drivers/Posix/Fl_Posix_System_Driver.H @@ -60,6 +60,7 @@ public: virtual int access(const char* f, int mode) { return ::access(f, mode);} virtual int stat(const char* f, struct stat *b) { return ::stat(f, b);} virtual char *getcwd(char* b, int l) {return ::getcwd(b, l);} + virtual int chdir(const char* path) {return ::chdir(path);} virtual int unlink(const char* f) {return ::unlink(f);} virtual int rmdir(const char* f) {return ::rmdir(f);} virtual int rename(const char* f, const char *n) {return ::rename(f, n);} diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H index 6b3a1e85e..36a7bf471 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H +++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.H @@ -58,6 +58,7 @@ public: virtual int access(const char* f, int mode); virtual int stat(const char* f, struct stat *b); virtual char *getcwd(char* b, int l); + virtual int chdir(const char* path); virtual int unlink(const char* f); virtual int mkdir(const char* f, int mode); virtual int rmdir(const char* f); diff --git a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx index 806a76b22..b1fb5a7e1 100644 --- a/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx +++ b/src/drivers/WinAPI/Fl_WinAPI_System_Driver.cxx @@ -64,6 +64,42 @@ extern "C" { int (*compar)(struct dirent **, struct dirent **)); } +/* + Convert a filename or path from UTF-8 to Windows wide character encoding (UTF-16). + + This helper function is used througout this file to convert UTF-8 strings + to Windows specific UTF-8 encoding for filenames and paths. + + The argument 'wbuf' must have been initialized with NULL or a previous call + to malloc() or realloc(). The global static variables above, particularly + wbuf, may be used to share one static pointer for many calls. Ideally + every call to this function would have its own static pointer though. + + If the converted string doesn't fit into the allocated size of 'wbuf' or + 'wbuf' is NULL a new buffer is allocated with realloc(). Hence, the pointer + 'wbuf' can be shared among multiple calls of this function if it has been + initialized with NULL (or malloc or realloc) before the first call. + + The return value is either the old value of 'wbuf' (if the string fits) + or a pointer at the (re)allocated buffer. + + Pseudo doxygen docs (static function intentionally not documented): + + param[in] path path to new working directory + param[in,out] wbuf pointer to string buffer (in) + new string (out, pointer may be changed) + + returns pointer to (new) string (buffer); may be changed +*/ +static wchar_t *path_to_wchar(const char *path, wchar_t *&wbuf) { + unsigned len = (unsigned)strlen(path); + unsigned wn = fl_utf8toUtf16(path, len, NULL, 0) + 1; // Query length + wbuf = (wchar_t *)realloc(wbuf, sizeof(wchar_t) * wn); + wn = fl_utf8toUtf16(path, len, (unsigned short *)wbuf, wn); // Convert string + wbuf[wn] = 0; + return wbuf; +} + /* Creates a driver that manages all system related calls. @@ -250,11 +286,15 @@ char *Fl_WinAPI_System_Driver::getcwd(char* b, int l) { } } -int Fl_WinAPI_System_Driver::unlink(const char* f) { - size_t l = strlen(f); - unsigned wn = fl_utf8toUtf16(f, (unsigned) l, NULL, 0) + 1; // Query length +int Fl_WinAPI_System_Driver::chdir(const char *path) { + return _wchdir(path_to_wchar(path, wbuf)); +} + +int Fl_WinAPI_System_Driver::unlink(const char *fname) { + size_t len = strlen(fname); + unsigned wn = fl_utf8toUtf16(fname, (unsigned)len, NULL, 0) + 1; // Query length wbuf = (wchar_t*)realloc(wbuf, sizeof(wchar_t)*wn); - wn = fl_utf8toUtf16(f, (unsigned) l, (unsigned short *)wbuf, wn); // Convert string + wn = fl_utf8toUtf16(fname, (unsigned)len, (unsigned short *)wbuf, wn); // Convert string wbuf[wn] = 0; return _wunlink(wbuf); } diff --git a/src/fl_utf8.cxx b/src/fl_utf8.cxx index 60d7614d6..a116cac9d 100644 --- a/src/fl_utf8.cxx +++ b/src/fl_utf8.cxx @@ -318,35 +318,38 @@ char *fl_getenv(const char* v) { This function is especially useful on the Windows platform where the standard open() function fails with UTF-8 encoded non-ASCII filenames. - \param f the UTF-8 encoded filename - \param oflags other arguments are as in the standard open() function - \return a file descriptor upon successful completion, or -1 in case of error. - \sa fl_fopen(), fl_open_ext(). + + \param[in] fname the UTF-8 encoded filename + \param[in] oflags other arguments are as in the standard open() function + \return a file descriptor upon successful completion, or -1 in case of error. + + \see fl_fopen(), fl_open_ext(). */ -int fl_open(const char* f, int oflags, ...) -{ +int fl_open(const char* fname, int oflags, ...) { int pmode; va_list ap; va_start(ap, oflags); pmode = va_arg (ap, int); va_end(ap); - return Fl::system_driver()->open(f, oflags, pmode); + return Fl::system_driver()->open(fname, oflags, pmode); } /** Cross-platform function to open files with a UTF-8 encoded name. + In comparison with fl_open(), this function allows to control whether the file is opened in binary (a.k.a. untranslated) mode. This is especially useful on the Windows platform where files are by default opened in text (translated) mode. - \param fname the UTF-8 encoded filename - \param binary if non-zero, the file is to be accessed in binary (a.k.a. - untranslated) mode. - \param oflags,... these arguments are as in the standard open() function. - Setting \p oflags to zero opens the file for reading. + + \param[in] fname the UTF-8 encoded filename + \param[in] binary if non-zero, the file is to be accessed in binary + (a.k.a. untranslated) mode. + \param[in] oflags,... these arguments are as in the standard open() function. + Setting \p oflags to zero opens the file for reading. + \return a file descriptor upon successful completion, or -1 in case of error. */ -int fl_open_ext(const char* fname, int binary, int oflags, ...) -{ +int fl_open_ext(const char* fname, int binary, int oflags, ...) { int pmode; va_list ap; va_start(ap, oflags); @@ -432,7 +435,36 @@ int fl_stat(const char* f, struct stat *b) { return Fl::system_driver()->stat(f, b); } -// TODO: add fl_chdir if we have fl_getcwd +/** Cross-platform function to change the current working directory, + given as a UTF-8 encoded string. + + This function is especially useful on the Windows platform where the + standard _wchdir() function needs a \p path in UTF-16 encoding. + + The \p path is converted to a system specific encoding if necessary + and the system specific \p chdir(converted_path) function is called. + + The function returns 0 on success and -1 on error. Depending on the + platform, \p errno \b may be set if an error occurs. + + \note The possible errno values are platform specific. Refer to the + documentation of the platform specific chdir() function. + + If the function is not implemented on a particular platform the + default implementation returns -1 and \p errno is \b not set. + + If the \p path is \p NULL the function returns -1, but \p errno is + \b not changed. This is a convenience feature of fl_chdir() as + opposed to chdir(). + + \param[in] path the target directory for chdir (may be \p NULL) + \return 0 if successful, -1 on error (errno may be set) +*/ +int fl_chdir(const char* path) { + if (!path) + return -1; + return Fl::system_driver()->chdir(path); +} /** Cross-platform function to get the current working directory as a UTF-8 encoded value. @@ -440,15 +472,19 @@ int fl_stat(const char* f, struct stat *b) { This function is especially useful on the Windows platform where the standard _wgetcwd() function returns UTF-16 encoded non-ASCII filenames. - \param b the buffer to populate - \param l the length of the buffer - \return the CWD encoded as UTF-8. + If \p buf is \p NULL a buffer of size \p (len+1) is allocated, filled with + the current working directory, and returned. In this case the buffer must + be released by the caller with free() to prevent memory leaks. + + \param[in] buf the buffer to populate (may be NULL) + \param[in] len the length of the buffer + \return the CWD encoded as UTF-8 */ -char *fl_getcwd(char* b, int l) { - if (b == NULL) { - b = (char*) malloc(l+1); +char *fl_getcwd(char *buf, int len) { + if (buf == NULL) { + buf = (char*)malloc(len + 1); } - return Fl::system_driver()->getcwd(b, l); + return Fl::system_driver()->getcwd(buf, len); } /** Cross-platform function to unlink() (that is, delete) a file using