/** \file wutil.c Wide character equivalents of various standard unix functions. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_LIBINTL_H #include #endif #include "fallback.h" #include "util.h" #include "common.h" #include "wutil.h" typedef std::string cstring; /** Minimum length of the internal covnersion buffers */ #define TMP_LEN_MIN 256 #ifndef PATH_MAX #ifdef MAXPATHLEN #define PATH_MAX MAXPATHLEN #else /** Fallback length of MAXPATHLEN. Just a hopefully sane value... */ #define PATH_MAX 4096 #endif #endif /** For wgettext: Number of string_buffer_t in the ring of buffers */ #define BUFF_COUNT 4 /* Lock to protect wgettext */ static pthread_mutex_t wgettext_lock; /* Maps string keys to (immortal) pointers to string values */ typedef std::map wgettext_map_t; static std::map wgettext_map; void wutil_init() { } void wutil_destroy() { } static pthread_mutex_t readdir_lock = PTHREAD_MUTEX_INITIALIZER; bool wreaddir_resolving(DIR *dir, const std::wstring &dir_path, std::wstring &out_name, bool *out_is_dir) { struct dirent *d = readdir( dir ); if ( !d ) return false; out_name = str2wcstring(d->d_name); if (out_is_dir) { bool is_dir; if (d->d_type == DT_DIR) { is_dir = true; } else if (d->d_type == DT_LNK) { /* We want to treat symlinks to directories as directories. Use stat to resolve it. */ cstring fullpath = wcs2string(dir_path); fullpath.push_back('/'); fullpath.append(d->d_name); struct stat buf; if (stat(fullpath.c_str(), &buf) != 0) { is_dir = false; } else { is_dir = !! (S_ISDIR(buf.st_mode)); } } else { is_dir = false; } *out_is_dir = is_dir; } return true; } bool wreaddir(DIR *dir, std::wstring &out_name) { struct dirent *d = readdir( dir ); if ( !d ) return false; out_name = str2wcstring(d->d_name); return true; } wchar_t *wgetcwd( wchar_t *buff, size_t sz ) { char *buffc = (char *)malloc( sz*MAX_UTF8_BYTES); char *res; wchar_t *ret = 0; if( !buffc ) { errno = ENOMEM; return 0; } res = getcwd( buffc, sz*MAX_UTF8_BYTES ); if( res ) { if( (size_t)-1 != mbstowcs( buff, buffc, sizeof( wchar_t ) * sz ) ) { ret = buff; } } free( buffc ); return ret; } int wchdir( const wcstring &dir ) { cstring tmp = wcs2string(dir); return chdir( tmp.c_str() ); } FILE *wfopen(const wcstring &path, const char *mode) { int permissions = 0, options = 0; switch (*mode++) { case 'r': permissions = O_RDONLY; break; case 'w': permissions = O_WRONLY; options = O_CREAT | O_TRUNC; break; case 'a': permissions = O_WRONLY; options = O_CREAT | O_APPEND; break; default: errno = EINVAL; return NULL; break; } /* Skip binary */ if (*mode == 'b') mode++; /* Consider append option */ if (*mode == '+') permissions = O_RDWR; int fd = wopen_cloexec(path, permissions | options, 0666); if (fd < 0) return NULL; FILE *result = fdopen(fd, mode); if (result == NULL) close(fd); return result; } FILE *wfreopen(const wcstring &path, const char *mode, FILE *stream) { cstring tmp = wcs2string(path); return freopen(tmp.c_str(), mode, stream); } bool set_cloexec(int fd) { int flags = fcntl(fd, F_GETFD, 0); if (flags < 0) { return false; } else if (flags & FD_CLOEXEC) { return true; } else { return fcntl(fd, F_SETFD, flags | FD_CLOEXEC) >= 0; } } static int wopen_internal(const wcstring &pathname, int flags, mode_t mode, bool cloexec) { cstring tmp = wcs2string(pathname); int fd = ::open(tmp.c_str(), flags, mode); if (cloexec && fd >= 0 && ! set_cloexec(fd)) { close(fd); fd = -1; } return fd; } int wopen(const wcstring &pathname, int flags, mode_t mode) { // off the main thread, always use wopen_cloexec ASSERT_IS_MAIN_THREAD(); return wopen_internal(pathname, flags, mode, false); } int wopen_cloexec(const wcstring &pathname, int flags, mode_t mode) { return wopen_internal(pathname, flags, mode, true); } int wcreat(const wcstring &pathname, mode_t mode) { cstring tmp = wcs2string(pathname); return creat(tmp.c_str(), mode); } DIR *wopendir(const wcstring &name) { cstring tmp = wcs2string(name); return opendir(tmp.c_str()); } int wstat(const wcstring &file_name, struct stat *buf) { cstring tmp = wcs2string(file_name); return stat(tmp.c_str(), buf); } int lwstat(const wcstring &file_name, struct stat *buf) { // fprintf(stderr, "%s\n", __PRETTY_FUNCTION__); cstring tmp = wcs2string(file_name); return lstat(tmp.c_str(), buf); } int waccess(const wcstring &file_name, int mode) { cstring tmp = wcs2string(file_name); return access(tmp.c_str(), mode); } int wunlink(const wcstring &file_name) { cstring tmp = wcs2string(file_name); return unlink(tmp.c_str()); } void wperror(const wcstring &s) { int e = errno; if( !s.empty() ) { fwprintf( stderr, L"%ls: ", s.c_str() ); } fwprintf( stderr, L"%s\n", strerror( e ) ); } #ifdef HAVE_REALPATH_NULL wchar_t *wrealpath(const wcstring &pathname, wchar_t *resolved_path) { cstring tmp = wcs2string(pathname); char *narrow_res = realpath( tmp.c_str(), 0 ); wchar_t *res; if( !narrow_res ) return 0; if( resolved_path ) { wchar_t *tmp2 = str2wcs( narrow_res ); wcslcpy( resolved_path, tmp2, PATH_MAX ); free( tmp2 ); res = resolved_path; } else { res = str2wcs( narrow_res ); } free( narrow_res ); return res; } #else wchar_t *wrealpath(const wchar_t *pathname, wchar_t *resolved_path) { cstring tmp = wcs2string(pathname); char narrow_buff[PATH_MAX]; char *narrow_res = realpath( tmp.c_str(), narrow_buff ); wchar_t *res; if( !narrow_res ) return 0; if( resolved_path ) { wchar_t *tmp2 = str2wcs( narrow_res ); wcslcpy( resolved_path, tmp2, PATH_MAX ); free( tmp2 ); res = resolved_path; } else { res = str2wcs( narrow_res ); } return res; } #endif wcstring wdirname( const wcstring &path ) { char *tmp = wcs2str(path.c_str()); char *narrow_res = dirname( tmp ); wcstring result = format_string(L"%s", narrow_res); free(tmp); return result; } wcstring wbasename( const wcstring &path ) { char *tmp = wcs2str(path.c_str()); char *narrow_res = basename( tmp ); wcstring result = format_string(L"%s", narrow_res); free(tmp); return result; } /* Really init wgettext */ static void wgettext_really_init() { pthread_mutex_init(&wgettext_lock, NULL); bindtextdomain( PACKAGE_NAME, LOCALEDIR ); textdomain( PACKAGE_NAME ); } /** For wgettext: Internal init function. Automatically called when a translation is first requested. */ static void wgettext_init_if_necessary() { static pthread_once_t once = PTHREAD_ONCE_INIT; pthread_once(&once, wgettext_really_init); } const wchar_t *wgettext( const wchar_t *in ) { if( !in ) return in; wgettext_init_if_necessary(); wcstring key = in; scoped_lock lock(wgettext_lock); wcstring *& val = wgettext_map[key]; if (val == NULL) { cstring mbs_in = wcs2string(key); char *out = gettext(mbs_in.c_str()); val = new wcstring(format_string(L"%s", out)); } return val->c_str(); } wcstring wgettext2(const wcstring &in) { wgettext_init_if_necessary(); std::string mbs_in = wcs2string(in); char *out = gettext( mbs_in.c_str() ); wcstring result = format_string(L"%s", out); return result; } const wchar_t *wgetenv( const wcstring &name ) { ASSERT_IS_MAIN_THREAD(); cstring name_narrow = wcs2string(name); char *res_narrow = getenv( name_narrow.c_str() ); static wcstring out; if( !res_narrow ) return 0; out = format_string(L"%s", res_narrow); return out.c_str(); } int wmkdir( const wcstring &name, int mode ) { cstring name_narrow = wcs2string(name); return mkdir( name_narrow.c_str(), mode ); } int wrename( const wcstring &old, const wcstring &newv ) { cstring old_narrow = wcs2string(old); cstring new_narrow =wcs2string(newv); return rename( old_narrow.c_str(), new_narrow.c_str() ); }