// // Util.cc for pekwm // Copyright © 2002-2009 Claes Nästén // // misc.cc for aewm++ // Copyright (C) 2000 Frank Hale // http://sapphire.sourceforge.net/ // // This program is licensed under the GNU GPL. // See the LICENSE file for more information. // #ifdef HAVE_CONFIG_H #include "config.h" #endif // HAVE_CONFIG_H #include #include #include #include #include #include #include #include extern "C" { #include #include #include #include #include #include } #include "Util.hh" using std::cerr; using std::endl; using std::ostringstream; using std::string; using std::transform; using std::vector; using std::wstring; using std::list; using std::ifstream; using std::ofstream; using std::find; using std::map; using std::getenv; using std::wcstombs; using std::wmemset; using std::mbstowcs; using std::exit; namespace Util { #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 255 #endif // HOST_NAME_MAX static iconv_t do_iconv_open(const char **from_names, const char **to_names); static size_t do_iconv (iconv_t ic, const char **inp, size_t *in_bytes, char **outp, size_t *out_bytes); // Initializers, members used for shared buffer unsigned int WIDE_STRING_COUNT = 0; iconv_t IC_TO_WC = reinterpret_cast(-1); iconv_t IC_TO_UTF8 = reinterpret_cast(-1); char *ICONV_BUF = 0; size_t ICONV_BUF_LEN = 0; // Constants, name of iconv internal names const char *ICONV_WC_NAMES[] = {"WCHAR_T", "UCS-4", 0}; const char *ICONV_UTF8_NAMES[] = {"UTF-8", "UTF8", 0}; const char *ICONV_UTF8_INVALID_STR = ""; const wchar_t *ICONV_WIDE_INVALID_STR = L""; // Constants, maximum number of bytes a single UTF-8 character can use. const size_t UTF8_MAX_BYTES = 6; //! @brief Fork and execute command with /bin/sh and execlp void forkExec(std::string command) { if (command.length() == 0) { #ifdef DEBUG cerr << __FILE__ << "@" << __LINE__ << ": " << "Util::forkExec() *** command length == 0" << endl; #endif // DEBUG return; } pid_t pid = fork(); switch (pid) { case 0: setsid(); execlp("/bin/sh", "sh", "-c", command.c_str(), (char *) 0); cerr << __FILE__ << "@" << __LINE__ << ": " << "Util::forkExec(" << command << ") execlp failed." << endl; exit(1); case -1: cerr << __FILE__ << "@" << __LINE__ << ": " << "Util::forkExec(" << command << ") fork failed." << endl; } } /** * Wrapper for gethostname returning a string instead of populating * char buffer. */ std::string getHostname(void) { string hostname; // Set WM_CLIENT_MACHINE char hostname_buf[HOST_NAME_MAX + 1]; if (! gethostname(hostname_buf, HOST_NAME_MAX)) { // Make sure it is null terminated hostname_buf[HOST_NAME_MAX] = '\0'; hostname = hostname_buf; } return hostname; } //! @brief Determines if the file exists bool isFile(const std::string &file) { if (file.size() == 0) { return false; } struct stat stat_buf; if (stat(file.c_str(), &stat_buf) == 0) { return (S_ISREG(stat_buf.st_mode)); } return false; } //! @brief Determines if the file is executable for the current user. bool isExecutable(const std::string &file) { if (file.size() == 0) { #ifdef DEBUG cerr << __FILE__ << "@" << __LINE__ << ": " << "Util::isExecutable() *** file length == 0" << endl; #endif // DEBUG return false; } struct stat stat_buf; if (! stat(file.c_str(), &stat_buf)) { if (stat_buf.st_uid == getuid()) { // user readable and executable if ((stat_buf.st_mode&S_IRUSR) && (stat_buf.st_mode&S_IXUSR)) { return true; } } if (getgid() == stat_buf.st_gid) { // group readable and executable if ((stat_buf.st_mode&S_IRGRP) && (stat_buf.st_mode&S_IXGRP)) { return true; } } if ((stat_buf.st_mode&S_IROTH) && (stat_buf.st_mode&S_IXOTH)) { return true; // other readable and executable } } return false; } /** * Get file mtime. */ time_t getMtime(const std::string &file) { struct stat stat_buf; if (! stat(file.c_str(), &stat_buf)) { return stat_buf.st_mtime; } else { return 0; } } /** * Check if file has different mtime than provided mtime. */ bool isFileChanged(const std::string &file, time_t &mtime) { time_t cur_mtime = getMtime(file); if (cur_mtime != mtime) { mtime = cur_mtime; return true; } return false; } /** * Check if old_file needs to be reloaded due to it being different * from new_file, path or mtime. */ bool requireReload(std::map &state, const std::string &file) { // Check for the file, signal reload if not previously loaded. map::iterator it(state.find(file)); if (it == state.end()) { return true; } // Check state of all files, if one is updated reload. for (it = state.begin(); it != state.end(); ++it) { if (isFileChanged(it->first, it->second)) { return true; } } return false; } /** * Copies a single text file. */ bool copyTextFile(const std::string &from, const std::string &to) { if ((from.length() == 0) || (to.length() == 0)) { return false; } ifstream stream_from(from.c_str()); if (! stream_from.good()) { cerr << __FILE__ << "@" << __LINE__ << ": " << "Can't copy: " << from << " to: " << to << endl; return false; } ofstream stream_to(to.c_str()); if (! stream_to.good()) { cerr << __FILE__ << "@" << __LINE__ << ": " << "Can't copy: " << from << " to: " << to << endl; return false; } stream_to << stream_from.rdbuf(); return true; } /** * Get name of the current user. */ string getUserName(void) { // Try to lookup current user with struct passwd *entry = getpwuid(geteuid()); if (entry && entry->pw_name) { return entry->pw_name; } else { if (getenv("USER")) { return getenv("USER"); } else { return "UNKNOWN"; } } } //! @brief Returns .extension of file std::string getFileExt(const std::string &file) { string::size_type pos = file.find_last_of('.'); if ((pos != string::npos) && (pos < file.size())) { return file.substr(pos + 1, file.size() - pos - 1); } else { return string(""); } } //! @brief Returns dir part of file std::string getDir(const std::string &file) { string::size_type pos = file.find_last_of('/'); if ((pos != string::npos) && (pos < file.size())) { return file.substr(0, pos); } else { return string(""); } } //! @brief Replaces the ~ with the complete homedir path. void expandFileName(std::string &file) { if (file.size() > 0) { if (file[0] == '~') { file.replace(0, 1, getenv("HOME")); } } } //! @brief Split the string str based on separator sep and put into vals //! //! This splits the string str into to max_tokens parts and puts in the vector //! vals. If max is 0 then it'll split it into as many tokens //! as possible, max defaults to 0. //! splitString returns the number of tokens it put into vals. //! //! @param str String to split //! @param vals Vector to put split values into //! @param sep Separators to use when splitting string //! @param max Maximum number of elements to put into vals (optional) //! @param include_empty Include empty elements, defaults to false. //! @return Number of tokens inserted into vals uint splitString(const std::string str, std::vector &toks, const char *sep, uint max, bool include_empty) { if (str.size() < 1) { return 0; } uint n = toks.size(); string::size_type s, e; s = str.find_first_not_of(" \t\n"); for (uint i = 0; ((i < max) || (max == 0)) && (s != string::npos); ++i) { e = str.find_first_of(sep, s); if ((e != string::npos) && (i < (max - 1))) { toks.push_back(str.substr(s, e - s)); } else if (s < str.size()) { toks.push_back(str.substr(s, str.size() - s)); break; } else { break; } if (include_empty) { s = str.find_first_of(sep, e + 1); if (s != (e + 1)) { s = str.find_first_not_of(sep, e); } } else { s = str.find_first_not_of(sep, e); } } return (toks.size() - n); } //! @brief Converts wide-character string to multibyte version //! @param str String to convert. //! @return Returns multibyte version of string. std::string to_mb_str(const std::wstring &str) { size_t ret, num = str.size() * 6 + 1; char *buf = new char[num]; memset(buf, '\0', num); ret = wcstombs(buf, str.c_str(), num); if (ret == static_cast(-1)) { cerr << " *** WARNING: failed to convert wide string to multibyte string" << endl; } string ret_str(buf); delete [] buf; return ret_str; } //! @brief Converts multibyte string to wide-character version //! @param str String to convert. //! @return Returns wide-character version of string. std::wstring to_wide_str(const std::string &str) { size_t ret, num = str.size() + 1; wchar_t *buf = new wchar_t[num]; wmemset(buf, L'\0', num); ret = mbstowcs(buf, str.c_str(), num); if (ret == static_cast(-1)) { cerr << " *** WARNING: failed to convert multibyte string to wide string" << endl; } wstring ret_str(buf); delete [] buf; return ret_str; } //! @brief Open iconv handle with to/from names. //! @param from_names null terminated list of from name alternatives. //! @param to_names null terminated list of to name alternatives. //! @return iconv_t handle on success, else -1. iconv_t do_iconv_open(const char **from_names, const char **to_names) { iconv_t ic = reinterpret_cast(-1); // Try all combinations of from/to name's to get a working // conversion handle. for (unsigned int i = 0; from_names[i]; ++i) { for (unsigned int j = 0; to_names[j]; ++j) { ic = iconv_open (to_names[j], from_names[i]); if (ic != reinterpret_cast(-1)) { #ifdef HAVE_ICONVCTL int int_value_one = 1; iconvctl(ic, ICONV_SET_DISCARD_ILSEQ, &int_value_one); #endif // HAVE_ICONVCTL return ic; } } } return ic; } //! @brief Iconv wrapper to hide different definitions of iconv. //! @param ic iconv handle. //! @param inp Input pointer. //! @param in_bytes Input bytes. //! @param outp Output pointer. //! @param out_bytes Output bytes. //! @return number of bytes converted irreversible or -1 on error. size_t do_iconv (iconv_t ic, const char **inp, size_t *in_bytes, char **outp, size_t *out_bytes) { #ifdef ICONV_CONST return iconv(ic, inp, in_bytes, outp, out_bytes); #else // !ICONV_CONST # ifdef __minix // this is as MESS across OS-es... return iconv(ic, (const char**)inp, in_bytes, outp, out_bytes); # else return iconv(ic, const_cast(inp), in_bytes, outp, out_bytes); # endif #endif // ICONV_CONST } //! @brief Init iconv conversion. void iconv_init (void) { // Cleanup previous init if any, being paranoid. iconv_deinit (); // Raise exception if this fails IC_TO_WC = do_iconv_open (ICONV_UTF8_NAMES, ICONV_WC_NAMES); IC_TO_UTF8 = do_iconv_open (ICONV_WC_NAMES, ICONV_UTF8_NAMES); // Equal mean if (IC_TO_WC != reinterpret_cast(-1) && IC_TO_UTF8 != reinterpret_cast(-1)) { // Create shared buffer. ICONV_BUF_LEN = 1024; ICONV_BUF = new char[ICONV_BUF_LEN]; } } //! @brief Deinit iconv conversion. void iconv_deinit (void) { // Cleanup resources if (IC_TO_WC != reinterpret_cast(-1)) { iconv_close (IC_TO_WC); } if (IC_TO_UTF8 != reinterpret_cast(-1)) { iconv_close (IC_TO_UTF8); } if (ICONV_BUF) { delete [] ICONV_BUF; } // Set members to safe values IC_TO_WC = reinterpret_cast(-1); IC_TO_UTF8 = reinterpret_cast(-1); ICONV_BUF = 0; ICONV_BUF_LEN = 0; } //! @brief Validate buffer size, grow if required. //! @param size Required size. void iconv_buf_grow (size_t size) { if (ICONV_BUF_LEN < size) { // Free resources, if any. if (ICONV_BUF) { delete [] ICONV_BUF; } // Calculate new buffer length and allocate new buffer for (; ICONV_BUF_LEN < size; ICONV_BUF_LEN *= 2) ; ICONV_BUF = new char[ICONV_BUF_LEN]; } } //! @brief Converts wide-character string to UTF-8 //! @param str String to convert. //! @return Returns UTF-8 representation of string. std::string to_utf8_str(const std::wstring &str) { string utf8_str; // Calculate length size_t in_bytes = str.size() * sizeof (wchar_t); size_t out_bytes = str.size() * UTF8_MAX_BYTES + 1; iconv_buf_grow(out_bytes); // Convert const char *inp = reinterpret_cast(str.c_str()); char *outp = ICONV_BUF; size_t len = do_iconv (IC_TO_UTF8, &inp, &in_bytes, &outp, &out_bytes); if (len != static_cast(-1)) { // Terminate string and cache result *outp = '\0'; utf8_str = ICONV_BUF; } else { cerr << " *** WARNING: to_utf8_str, failed with error " << strerror (errno) << endl; utf8_str = ICONV_UTF8_INVALID_STR; } return utf8_str; } //! @brief Converts to wide string from UTF-8 //! @param str String to convert. //! @return Returns wide representation of string. std::wstring from_utf8_str(const std::string &str) { wstring wide_str; // Calculate length size_t in_bytes = str.size(); size_t out_bytes = (in_bytes + 1) * sizeof(wchar_t); iconv_buf_grow(out_bytes); // Convert const char *inp = str.c_str(); char *outp = ICONV_BUF; size_t len = do_iconv(IC_TO_WC, &inp, &in_bytes, &outp, &out_bytes); if (len != static_cast(-1)) { // Terminate string and cache result *reinterpret_cast(outp) = L'\0'; wide_str = reinterpret_cast(ICONV_BUF); } else { cerr << " *** WARNING: from_utf8_str, failed on string \"" << str << "\"." << endl; wide_str = ICONV_WIDE_INVALID_STR; } return wide_str; } /** * Add object to list making sure there are no duplicates. */ void file_backed_list::push_back_unique(const std::wstring &entry) { list::iterator it(find(begin(), end(), entry)); if (it != end()) { erase(it); } push_back(entry); } /** * Load list from file, updating _path if set. * * @param path Load data from path. * @return Number of elements loaded. */ unsigned int file_backed_list::load (const std::string &path) { unsigned int loaded = 0; ifstream ifile; ifile.open(path.c_str()); if (ifile.is_open()) { // Update only path if successfully opened. _path = path; string mb_line; while (ifile.good()) { getline(ifile, mb_line); if (mb_line.size()) { push_back(to_wide_str(mb_line)); ++loaded; } } ifile.close(); } return loaded; } /** * Save list from file overwriting previous data. */ bool file_backed_list::save (const std::string &path) { bool status = false; ofstream ofile(path.c_str()); if (ofile.is_open()) { // Update path if successfully opened. _path = path; string line; list::iterator it(begin ()); for (; it != end(); ++it) { ofile << to_utf8_str(*it) << "\n"; } ofile.close(); status = true; } return status; } } // end namespace Util.