From b1c301dc1ca62287180afc9d7fef7e548e120f1d Mon Sep 17 00:00:00 2001 From: Andrzej Rybczak Date: Wed, 29 Aug 2012 11:46:17 +0200 Subject: [PATCH] split majority of helpers and rewrite a few heinous functions --- src/Makefile.am | 10 +- src/actions.cpp | 24 ++- src/actions.h | 1 + src/browser.cpp | 7 +- src/conv.cpp | 79 ---------- src/conv.h | 13 -- src/display.cpp | 7 +- src/error.cpp | 2 +- src/helpers.cpp | 141 +---------------- src/helpers.h | 64 +------- src/lastfm.cpp | 2 +- src/lastfm_service.cpp | 9 +- src/lyrics_fetcher.cpp | 19 +-- src/media_library.cpp | 7 +- src/mutable_song.cpp | 2 +- src/ncmpcpp.cpp | 2 +- src/playlist.cpp | 1 + src/playlist_editor.cpp | 1 + src/search_engine.cpp | 1 + src/sel_items_adder.cpp | 1 + src/settings.cpp | 11 +- src/status.cpp | 2 +- src/strbuffer.h | 10 +- src/tag_editor.cpp | 7 +- src/tiny_tag_editor.cpp | 2 +- src/utility/comparators.cpp | 75 +++++++++ .../comparators.h} | 59 ++++--- src/{tolower.cpp => utility/html.cpp} | 65 ++++++-- src/{tolower.h => utility/html.h} | 10 +- src/utility/string.cpp | 146 ++++++++++++++++++ src/{string_utilities.h => utility/string.h} | 20 ++- 31 files changed, 427 insertions(+), 373 deletions(-) create mode 100644 src/utility/comparators.cpp rename src/{string_utilities.cpp => utility/comparators.h} (61%) rename src/{tolower.cpp => utility/html.cpp} (54%) rename src/{tolower.h => utility/html.h} (90%) create mode 100644 src/utility/string.cpp rename src/{string_utilities.h => utility/string.h} (74%) diff --git a/src/Makefile.am b/src/Makefile.am index a77134fc..741bbaee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,8 @@ bin_PROGRAMS = ncmpcpp ncmpcpp_SOURCES = \ + utility/comparators.cpp \ + utility/html.cpp \ + utility/string.cpp \ actions.cpp \ browser.cpp \ charset.cpp \ @@ -32,10 +35,8 @@ ncmpcpp_SOURCES = \ song.cpp \ song_info.cpp \ status.cpp \ - string_utilities.cpp \ tag_editor.cpp \ tiny_tag_editor.cpp \ - tolower.cpp \ visualizer.cpp \ window.cpp @@ -45,6 +46,9 @@ INCLUDES= $(all_includes) # the library search path. ncmpcpp_LDFLAGS = $(all_libraries) noinst_HEADERS = \ + utility/comparators.h \ + utility/html.h \ + utility/string.h \ browser.h \ charset.h \ clock.h \ @@ -75,9 +79,7 @@ noinst_HEADERS = \ settings.h \ song.h \ song_info.h \ - string_utilities.h \ tag_editor.h \ tiny_tag_editor.h \ - tolower.h \ visualizer.h \ window.h diff --git a/src/actions.cpp b/src/actions.cpp index dd3dba5c..e51330ee 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -28,6 +28,7 @@ #include "display.h" #include "global.h" #include "mpdpp.h" +#include "utility/comparators.h" #include "browser.h" #include "clock.h" @@ -42,6 +43,7 @@ #include "server_info.h" #include "song_info.h" #include "outputs.h" +#include "utility/string.h" #include "tag_editor.h" #include "tiny_tag_editor.h" #include "visualizer.h" @@ -395,6 +397,18 @@ void Action::ListsChangeFinisher() } } +bool Action::ConnectToMPD() +{ + if (!Mpd.Connect()) + { + std::cout << "Couldn't connect to MPD "; + std::cout << "(host = " << Mpd.GetHostname() << ", port = " << Mpd.GetPort() << ")"; + std::cout << ": " << Mpd.GetErrorMessage() << std::endl; + return false; + } + return true; +} + bool Action::AskYesNoQuestion(const std::string &question, void (*callback)()) { using Global::wFooter; @@ -1433,7 +1447,7 @@ void EditLibraryTag::Run() if (dir_to_update.empty()) dir_to_update = es.getDirectory(); else - FindSharedDir(es.getDirectory(), dir_to_update); + getSharedDirectory(es.getDirectory(), dir_to_update); }); if (success) { @@ -1492,7 +1506,7 @@ void EditLibraryAlbum::Run() } if (success) { - Mpd.UpdateDirectory(FindSharedDir(myLibrary->Songs)); + Mpd.UpdateDirectory(getSharedDirectory(myLibrary->Songs)); ShowMessage("Tags updated successfully"); } } @@ -1541,7 +1555,7 @@ void EditDirectoryName::Run() const char msg[] = "Directory renamed to \"%s\""; ShowMessage(msg, Shorten(TO_WSTRING(new_dir), COLS-static_strlen(msg)).c_str()); if (!myBrowser->isLocal()) - Mpd.UpdateDirectory(locale_to_utf_cpy(FindSharedDir(old_dir, new_dir))); + Mpd.UpdateDirectory(locale_to_utf_cpy(getSharedDirectory(old_dir, new_dir))); myBrowser->GetDirectory(myBrowser->CurrentDir()); } else @@ -2151,7 +2165,7 @@ void AddRandomItems::Run() mpd_tag_type tag_type = IntoTagItem(answer); std::string tag_type_str = answer == 's' ? "song" : IntoStr(tag_type); - ToLower(tag_type_str); + lowercase(tag_type_str); LockStatusbar(); Statusbar() << "Number of random " << tag_type_str << "s: "; @@ -2218,7 +2232,7 @@ void ToggleLibraryTagType::Run() std::string item_type = IntoStr(Config.media_lib_primary_tag); myLibrary->Artists->SetTitle(Config.titles_visibility ? item_type + "s" : ""); myLibrary->Artists->Reset(); - ToLower(item_type); + lowercase(item_type); if (myLibrary->Columns() == 2) { myLibrary->Songs->Clear(); diff --git a/src/actions.h b/src/actions.h index 3b4f7fa5..fc4141c6 100644 --- a/src/actions.h +++ b/src/actions.h @@ -109,6 +109,7 @@ struct Action static void ResizeScreen(); static void SetWindowsDimensions(); + static bool ConnectToMPD(); static bool AskYesNoQuestion(const std::string &question, void (*callback)()); static bool isMPDMusicDirSet(); diff --git a/src/browser.cpp b/src/browser.cpp index ce48ebe7..1c082f58 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -32,6 +32,7 @@ #include "playlist.h" #include "settings.h" #include "status.h" +#include "utility/comparators.h" #ifdef HAVE_TAGLIB_H # include "tag_editor.h" #endif // HAVE_TAGLIB_H @@ -325,7 +326,7 @@ bool Browser::hasSupportedExtension(const std::string &file) return false; std::string ext = file.substr(last_dot+1); - ToLower(ext); + lowercase(ext); return SupportedExtensions.find(ext) != SupportedExtensions.end(); } @@ -594,7 +595,7 @@ std::string Browser::ItemToString(const MPD::Item &item, void *) { case MPD::itDirectory: { - return "[" + ExtractTopName(item.name) + "]"; + return "[" + getBasename(item.name) + "]"; } case MPD::itSong: { @@ -605,7 +606,7 @@ std::string Browser::ItemToString(const MPD::Item &item, void *) } case MPD::itPlaylist: { - return Config.browser_playlist_prefix.Str() + ExtractTopName(item.name); + return Config.browser_playlist_prefix.Str() + getBasename(item.name); } default: { diff --git a/src/conv.cpp b/src/conv.cpp index cdff8fc1..06b8a70d 100644 --- a/src/conv.cpp +++ b/src/conv.cpp @@ -293,82 +293,3 @@ void EscapeUnallowedChars(std::string &s) } } } - -std::string unescapeHtmlUtf8(const std::string &data) -{ - std::string result; - for (size_t i = 0, j; i < data.length(); ++i) - { - if (data[i] == '&' && data[i+1] == '#' && (j = data.find(';', i)) != std::string::npos) - { - int n = atoi(&data.c_str()[i+2]); - if (n >= 0x800) - { - result += (0xe0 | ((n >> 12) & 0x0f)); - result += (0x80 | ((n >> 6) & 0x3f)); - result += (0x80 | (n & 0x3f)); - } - else if (n >= 0x80) - { - result += (0xc0 | ((n >> 6) & 0x1f)); - result += (0x80 | (n & 0x3f)); - } - else - result += n; - i = j; - } - else - result += data[i]; - } - return result; -} - -void StripHtmlTags(std::string &s) -{ - bool erase = 0; - for (size_t i = s.find("<"); i != std::string::npos; i = s.find("<")) - { - size_t j = s.find(">", i)+1; - s.replace(i, j-i, ""); - } - Replace(s, "'", "'"); - Replace(s, "&", "&"); - Replace(s, """, "\""); - Replace(s, " ", " "); - for (size_t i = 0; i < s.length(); ++i) - { - if (erase) - { - s.erase(s.begin()+i); - erase = 0; - } - if (s[i] == 13) // ascii code for windows line ending, get rid of this shit - { - s[i] = '\n'; - erase = 1; - } - else if (s[i] == '\t') - s[i] = ' '; - } -} - -void Trim(std::string &s) -{ - if (s.empty()) - return; - - size_t b = 0; - size_t e = s.length()-1; - - while (s[e] == ' ' || s[e] == '\n') - --e; - ++e; - if (e != s.length()) - s.resize(e); - - while (s[b] == ' ' || s[b] == '\n') - ++b; - if (b != 0) - s = s.substr(b); -} - diff --git a/src/conv.h b/src/conv.h index f0dc75c6..39c3f1b4 100644 --- a/src/conv.h +++ b/src/conv.h @@ -35,13 +35,6 @@ template inline size_t static_strlen(const char (&)[N]) return N-1; } -template void Replace(std::string &s, const char (&from)[N], const char *to) -{ - size_t to_len = strlen(to); - for (size_t i = 0; (i = s.find(from, i)) != std::string::npos; i += to_len) - s.replace(i, N-1, to); -} - int StrToInt(const std::string &); long StrToLong(const std::string &); @@ -63,11 +56,5 @@ std::string Shorten(const std::basic_string &s, size_t max_length); void EscapeUnallowedChars(std::string &); -std::string unescapeHtmlUtf8(const std::string &data); - -void StripHtmlTags(std::string &s); - -void Trim(std::string &s); - #endif diff --git a/src/display.cpp b/src/display.cpp index d3725574..1175459e 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -352,12 +352,11 @@ void Display::Tags(const MPD::MutableSong &s, void *data, Menu } } -void Display::Outputs (const MPD::Output &o, void * , Menu< MPD::Output > *menu) +void Display::Outputs(const MPD::Output &o, void * , Menu< MPD::Output > *menu) { *menu << o.name(); } - void Display::Items(const MPD::Item &item, void *data, Menu *menu) { switch (item.type) @@ -369,7 +368,7 @@ void Display::Items(const MPD::Item &item, void *data, Menu *menu) *menu << "[..]"; return; } - *menu << "[" << ExtractTopName(item.name) << "]"; + *menu << "[" << getBasename(item.name) << "]"; return; } case MPD::itSong: @@ -379,7 +378,7 @@ void Display::Items(const MPD::Item &item, void *data, Menu *menu) Display::SongsInColumns(item.song, data, reinterpret_cast *>(menu)); return; case MPD::itPlaylist: - *menu << Config.browser_playlist_prefix << ExtractTopName(item.name); + *menu << Config.browser_playlist_prefix << getBasename(item.name); return; default: return; diff --git a/src/error.cpp b/src/error.cpp index 2da3e771..06671106 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -28,7 +28,7 @@ namespace { const char *Timestamp() { - static char result[32]; + char result[32]; time_t raw; tm *t; time(&raw); diff --git a/src/helpers.cpp b/src/helpers.cpp index 26425c97..ee1722ab 100644 --- a/src/helpers.cpp +++ b/src/helpers.cpp @@ -39,16 +39,6 @@ #include "outputs.h" #include "visualizer.h" -bool ConnectToMPD() -{ - if (!Mpd.Connect()) - { - std::cout << "Couldn't connect to MPD (host = " << Mpd.GetHostname() << ", port = " << Mpd.GetPort() << "): " << Mpd.GetErrorMessage() << std::endl; - return false; - } - return true; -} - void ParseArgv(int argc, char **argv) { bool quit = 0; @@ -143,7 +133,7 @@ void ParseArgv(int argc, char **argv) exit(0); } - if (!ConnectToMPD()) + if (!Action::ConnectToMPD()) exit(1); if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--screen")) @@ -203,8 +193,8 @@ void ParseArgv(int argc, char **argv) now_playing_format = "{"; now_playing_format += argv[i]; now_playing_format += "}"; - Replace(now_playing_format, "\\n", "\n"); - Replace(now_playing_format, "\\t", "\t"); + replace(now_playing_format, "\\n", "\n"); + replace(now_playing_format, "\\t", "\t"); } } std::cout << utf_to_locale_cpy(Mpd.GetCurrentlyPlayingSong().toString(now_playing_format)) << "\n"; @@ -280,59 +270,6 @@ void ParseArgv(int argc, char **argv) exit(0); } -int CaseInsensitiveStringComparison::operator()(const std::string &a, const std::string &b) -{ - const char *i = a.c_str(); - const char *j = b.c_str(); - if (Config.ignore_leading_the) - { - if (hasTheWord(a)) - i += 4; - if (hasTheWord(b)) - j += 4; - } - int dist; - while (!(dist = tolower(*i)-tolower(*j)) && *j) - ++i, ++j; - return dist; -} - -bool CaseInsensitiveSorting::operator()(const MPD::Item &a, const MPD::Item &b) -{ - bool result = false; - if (a.type == b.type) - { - switch (a.type) - { - case MPD::itDirectory: - result = cmp(ExtractTopName(a.name), ExtractTopName(b.name)) < 0; - break; - case MPD::itPlaylist: - result = cmp(a.name, b.name) < 0; - break; - case MPD::itSong: - switch (Config.browser_sort_mode) - { - case smName: - result = operator()(a.song, b.song); - break; - case smMTime: - result = a.song.getMTime() > b.song.getMTime(); - break; - case smCustomFormat: - result = cmp(a.song.toString(Config.browser_sort_format), b.song.toString(Config.browser_sort_format)) < 0; - break; - } - break; - default: // there is no other option, silence compiler - assert(false); - } - } - else - result = a.type < b.type; - return result; -} - std::string Timestamp(time_t t) { char result[32]; @@ -365,10 +302,10 @@ void UpdateSongList(Menu *menu) } #ifdef HAVE_TAGLIB_H -std::string FindSharedDir(const MPD::SongList &v) +std::string getSharedDirectory(const MPD::SongList &v) { if (v.empty()) // this should never happen, but in case... - FatalError("empty SongList passed to FindSharedDir(const SongList &)!"); + FatalError("empty SongList passed to getSharedDirectory(const SongList &)!"); size_t i = -1; std::string first = v.front().getDirectory(); for (MPD::SongList::const_iterator it = ++v.begin(); it != v.end(); ++it) @@ -384,63 +321,6 @@ std::string FindSharedDir(const MPD::SongList &v) } #endif // HAVE_TAGLIB_H -std::string FindSharedDir(const std::string &one, const std::string &two) -{ - if (one == two) - return one; - size_t i = 0; - while (!one.compare(i, 1, two, i, 1)) - ++i; - i = one.rfind("/", i); - return i != std::string::npos ? one.substr(0, i) : "/"; -} - -std::string GetLineValue(std::string &line, char a, char b, bool once) -{ - int pos[2] = { -1, -1 }; - char x = a; - size_t i = 0; - while ((i = line.find(x, i)) != std::string::npos && pos[1] < 0) - { - if (i && line[i-1] == '\\') - { - i++; - continue; - } - if (once) - line[i] = 0; - pos[pos[0] >= 0] = i++; - if (x == a) - x = b; - } - ++pos[0]; - std::string result = pos[0] >= 0 && pos[1] >= 0 ? line.substr(pos[0], pos[1]-pos[0]) : ""; - - // replace \a and \b with a and b respectively - char r1[] = "\\ ", r2[] = " "; - r1[1] = r2[0] = a; - Replace(result, r1, r2); - if (a != b) - { - r1[1] = r2[0] = b; - Replace(result, r1, r2); - } - - return result; -} - -std::string ExtractTopName(const std::string &s) -{ - size_t slash = s.rfind("/"); - return slash != std::string::npos ? s.substr(++slash) : s; -} - -std::string PathGoDownOneLevel(const std::string &path) -{ - size_t i = path.rfind('/'); - return i == std::string::npos ? "/" : path.substr(0, i); -} - std::basic_string Scroller(const std::basic_string &str, size_t &pos, size_t width) { std::basic_string s(str); @@ -473,14 +353,3 @@ std::basic_string Scroller(const std::basic_string &str, s result = s; return result; } - -bool isInteger(const char *s) -{ - assert(s); - if (*s == '\0') - return false; - for (const char *it = s; *it != '\0'; ++it) - if (!isdigit(*it) && (it != s || *it != '-')) - return false; - return true; -} diff --git a/src/helpers.h b/src/helpers.h index 88fcf76a..73d40181 100644 --- a/src/helpers.h +++ b/src/helpers.h @@ -27,47 +27,8 @@ #include "settings.h" #include "status.h" -bool ConnectToMPD(); void ParseArgv(int, char **); -class CaseInsensitiveStringComparison -{ - bool hasTheWord(const std::string &s) - { - return (s.length() > 3) - && (s[0] == 't' || s[0] == 'T') - && (s[1] == 'h' || s[1] == 'H') - && (s[2] == 'e' || s[2] == 'E') - && (s[3] == ' '); - } - - public: - int operator()(const std::string &a, const std::string &b); -}; - -class CaseInsensitiveSorting -{ - CaseInsensitiveStringComparison cmp; - - public: - bool operator()(const std::string &a, const std::string &b) - { - return cmp(a, b) < 0; - } - - bool operator()(const MPD::Song &a, const MPD::Song &b) - { - return cmp(a.getName(), b.getName()) < 0; - } - - template bool operator()(const std::pair &a, const std::pair &b) - { - return cmp(a.first, b.first) < 0; - } - - bool operator()(const MPD::Item &, const MPD::Item &); -}; - template std::string StringPairToString(const std::pair &pair, void *) { return pair.first; @@ -211,39 +172,26 @@ template void ShowTag(T &buf, const std::string &tag) buf << tag; } -inline bool Keypressed(int in, const int *key) -{ - return in == key[0] || in == key[1]; -} - std::string Timestamp(time_t t); void UpdateSongList(Menu *); -std::string FindSharedDir(const std::string &, const std::string &); #ifdef HAVE_TAGLIB_H -template std::string FindSharedDir(Menu *menu) +template std::string getSharedDirectory(Menu *menu) { assert(!menu->Empty()); std::string dir; dir = (*menu)[0].getDirectory(); for (size_t i = 1; i < menu->Size(); ++i) - dir = FindSharedDir(dir, (*menu)[i].getDirectory()); + { + dir = getSharedDirectory(dir, (*menu)[i].getDirectory()); + if (dir == "/") + break; + } return dir; } -std::string FindSharedDir(const MPD::SongList &); #endif // HAVE_TAGLIB_H -std::string ExtractTopName(const std::string &); -std::string PathGoDownOneLevel(const std::string &path); - -std::string GetLineValue(std::string &, char = '"', char = '"', bool = 0); - std::basic_string Scroller(const std::basic_string &str, size_t &pos, size_t width); -bool askYesNoQuestion(const Buffer &question, void (*callback)()); - -bool isInteger(const char *s); - #endif - diff --git a/src/lastfm.cpp b/src/lastfm.cpp index 27db93cc..a0a9bf9a 100644 --- a/src/lastfm.cpp +++ b/src/lastfm.cpp @@ -124,7 +124,7 @@ void Lastfm::Load() locale_to_utf(artist); std::string file = artist + ".txt"; - ToLower(file); + lowercase(file); EscapeUnallowedChars(file); itsFilename = itsFolder + "/" + file; diff --git a/src/lastfm_service.cpp b/src/lastfm_service.cpp index ad248817..81d72d0b 100644 --- a/src/lastfm_service.cpp +++ b/src/lastfm_service.cpp @@ -25,6 +25,7 @@ #include "conv.h" #include "curl_handle.h" #include "settings.h" +#include "utility/html.h" const char *LastfmService::baseURL = "http://ws.audioscrobbler.com/2.0/?api_key=d94e5b6e26469a2d1ffae8ef20131b79&method="; @@ -55,7 +56,7 @@ LastfmService::Result LastfmService::fetch(Args &args) if (actionFailed(data)) { - StripHtmlTags(data); + stripHtmlTags(data); result.second = data; return result; } @@ -91,8 +92,8 @@ bool LastfmService::actionFailed(const std::string &data) void LastfmService::postProcess(std::string &data) { - StripHtmlTags(data); - Trim(data); + stripHtmlTags(data); + trim(data); } /***********************************************************************/ @@ -147,7 +148,7 @@ bool ArtistInfo::parse(std::string &data) k += static_strlen(""); similars.push_back(std::make_pair(data.substr(i, j-i), data.substr(k, l-k))); - StripHtmlTags(similars.back().first); + stripHtmlTags(similars.back().first); } a += static_strlen("", "\n"); - StripHtmlTags(data); - Trim(data); + replace(data, "
", "\n"); + stripHtmlTags(data); + trim(data); result.second = data; result.first = true; @@ -225,8 +226,8 @@ void MetrolyricsFetcher::postProcess(std::string &data) // some of lyrics have both \n chars and
, html tags // are always present whereas \n chars are not, so we need to // throw them away to avoid having line breaks doubled. - Replace(data, " ", ""); - Replace(data, "
", "\n"); + replace(data, " ", ""); + replace(data, "
", "\n"); data = unescapeHtmlUtf8(data); LyricsFetcher::postProcess(data); } diff --git a/src/media_library.cpp b/src/media_library.cpp index 6eea5ad9..3b36c052 100644 --- a/src/media_library.cpp +++ b/src/media_library.cpp @@ -29,6 +29,7 @@ #include "mpdpp.h" #include "playlist.h" #include "status.h" +#include "utility/comparators.h" using Global::MainHeight; using Global::MainStartY; @@ -162,7 +163,7 @@ void MediaLibrary::SwitchTo() if (Config.titles_visibility) { std::string item_type = IntoStr(Config.media_lib_primary_tag); - ToLower(item_type); + lowercase(item_type); Albums->SetTitle("Albums (sorted by " + item_type + ")"); } else @@ -635,7 +636,7 @@ void MediaLibrary::LocateSong(const MPD::Song &s) if (primary_tag.empty()) { std::string item_type = IntoStr(Config.media_lib_primary_tag); - ToLower(item_type); + lowercase(item_type); ShowMessage("Can't use this function because the song has no %s tag set", item_type.c_str()); return; } @@ -726,7 +727,7 @@ void MediaLibrary::AddToPlaylist(bool add_n_play) || (w == Albums && Albums->Current().Date == AllTracksMarker)) { std::string tag_type = IntoStr(Config.media_lib_primary_tag); - ToLower(tag_type); + lowercase(tag_type); ShowMessage("Songs with %s = \"%s\" added", tag_type.c_str(), Artists->Current().c_str()); } else if (w == Albums) diff --git a/src/mutable_song.cpp b/src/mutable_song.cpp index 1e301b5b..60126337 100644 --- a/src/mutable_song.cpp +++ b/src/mutable_song.cpp @@ -19,7 +19,7 @@ ***************************************************************************/ #include "mutable_song.h" -#include "string_utilities.h" +#include "utility/string.h" namespace MPD {// diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 879aff7c..86c65fa6 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -116,7 +116,7 @@ int main(int argc, char **argv) if (argc > 1) ParseArgv(argc, argv); - if (!ConnectToMPD()) + if (!Action::ConnectToMPD()) exit(1); if (Mpd.Version() < 14) diff --git a/src/playlist.cpp b/src/playlist.cpp index b0bb7d1d..3d6fb67d 100644 --- a/src/playlist.cpp +++ b/src/playlist.cpp @@ -27,6 +27,7 @@ #include "playlist.h" #include "song.h" #include "status.h" +#include "utility/comparators.h" using Global::MainHeight; using Global::MainStartY; diff --git a/src/playlist_editor.cpp b/src/playlist_editor.cpp index 961adb51..bca676ec 100644 --- a/src/playlist_editor.cpp +++ b/src/playlist_editor.cpp @@ -30,6 +30,7 @@ #include "mpdpp.h" #include "status.h" #include "tag_editor.h" +#include "utility/comparators.h" using Global::MainHeight; using Global::MainStartY; diff --git a/src/search_engine.cpp b/src/search_engine.cpp index 1c545ba5..031dedf8 100644 --- a/src/search_engine.cpp +++ b/src/search_engine.cpp @@ -27,6 +27,7 @@ #include "search_engine.h" #include "settings.h" #include "status.h" +#include "utility/comparators.h" using Global::MainHeight; using Global::MainStartY; diff --git a/src/sel_items_adder.cpp b/src/sel_items_adder.cpp index 2d2749d7..a6271aca 100644 --- a/src/sel_items_adder.cpp +++ b/src/sel_items_adder.cpp @@ -26,6 +26,7 @@ #include "playlist.h" #include "playlist_editor.h" #include "sel_items_adder.h" +#include "utility/comparators.h" using Global::MainHeight; using Global::MainStartY; diff --git a/src/settings.cpp b/src/settings.cpp index 34de53f1..f8bf685e 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -104,7 +104,7 @@ namespace if (equal == std::string::npos) return ""; std::string result = s.substr(0, equal); - Trim(result); + trim(result); return result; } @@ -445,7 +445,7 @@ void Configuration::Read() if (!cl.empty() && cl[0] != '#') { name = GetOptionName(cl); - v = GetLineValue(cl); + v = getEnclosedString(cl, '"', '"', 0); if (name == "ncmpcpp_directory") { @@ -1078,11 +1078,12 @@ void Configuration::GenerateColumns() { columns.clear(); std::string width; - while (!(width = GetLineValue(song_list_columns_format, '(', ')', 1)).empty()) + size_t pos = 0; + while (!(width = getEnclosedString(song_list_columns_format, '(', ')', &pos)).empty()) { Column col; - col.color = IntoColor(GetLineValue(song_list_columns_format, '[', ']', 1)); - std::string tag_type = GetLineValue(song_list_columns_format, '{', '}', 1); + col.color = IntoColor(getEnclosedString(song_list_columns_format, '[', ']', &pos)); + std::string tag_type = getEnclosedString(song_list_columns_format, '{', '}', &pos); col.fixed = *width.rbegin() == 'f'; diff --git a/src/status.cpp b/src/status.cpp index 23ac35f8..f9c0c477 100644 --- a/src/status.cpp +++ b/src/status.cpp @@ -200,7 +200,7 @@ void NcmpcppErrorCallback(MPD::Connection *, int errorid, const char *msg, void } else if ((errorid >> 8) == MPD_SERVER_ERROR_NO_EXIST && myScreen == myBrowser) { - myBrowser->GetDirectory(PathGoDownOneLevel(myBrowser->CurrentDir())); + myBrowser->GetDirectory(getParentDirectory(myBrowser->CurrentDir())); myBrowser->Refresh(); } else diff --git a/src/strbuffer.h b/src/strbuffer.h index b64cbb97..a3ca15c1 100644 --- a/src/strbuffer.h +++ b/src/strbuffer.h @@ -22,8 +22,8 @@ #define _STRBUFFER_H #include "numeric_conversions.h" -#include "tolower.h" #include "window.h" +#include "utility/string.h" #include @@ -260,8 +260,8 @@ template bool basic_buffer::SetFormatting( std::basic_string base = itsString; if (!case_sensitive) { - ToLower(s); - ToLower(base); + lowercase(s); + lowercase(base); } FormatPos fp; for (size_t i = base.find(s); i != std::basic_string::npos; i = base.find(s, i)) @@ -294,8 +294,8 @@ template void basic_buffer::RemoveFormatting( std::basic_string base = itsString; if (!case_sensitive) { - ToLower(pattern); - ToLower(base); + lowercase(pattern); + lowercase(base); } FormatPos fp; for (size_t i = base.find(pattern); i != std::basic_string::npos; i = base.find(pattern, i)) diff --git a/src/tag_editor.cpp b/src/tag_editor.cpp index eb0868d2..d4cc9bb4 100644 --- a/src/tag_editor.cpp +++ b/src/tag_editor.cpp @@ -38,6 +38,7 @@ #include "global.h" #include "song_info.h" #include "playlist.h" +#include "utility/comparators.h" using Global::myScreen; using Global::MainHeight; @@ -637,7 +638,7 @@ void TagEditor::EnterPressed() w->Refresh(); w = LeftColumn; LeftColumn->HighlightColor(Config.active_column_color); - Mpd.UpdateDirectory(FindSharedDir(Tags)); + Mpd.UpdateDirectory(getSharedDirectory(Tags)); } else Tags->Clear(); @@ -927,7 +928,7 @@ void TagEditor::LocateSong(const MPD::Song &s) Dirs->Reset(); // go to the first pos, which is "." (music dir root) // highlight directory we need and get files from it - std::string dir = ExtractTopName(s.getDirectory()); + std::string dir = getBasename(s.getDirectory()); for (size_t i = 0; i < Dirs->Size(); ++i) { if ((*Dirs)[i].first == dir) @@ -1131,7 +1132,7 @@ void TagEditor::LowerAllLetters(MPD::MutableSong &s) unsigned i = 0; for (std::string tag; !(tag = (s.*m->Get)(i)).empty(); ++i) { - ToLower(tag); + lowercase(tag); (s.*m->Set)(tag, i); } } diff --git a/src/tiny_tag_editor.cpp b/src/tiny_tag_editor.cpp index 180b1a70..e7b9b93c 100644 --- a/src/tiny_tag_editor.cpp +++ b/src/tiny_tag_editor.cpp @@ -188,7 +188,7 @@ bool TinyTagEditor::getTags() std::string ext = itsEdited.getURI(); ext = ext.substr(ext.rfind(".")+1); - ToLower(ext); + lowercase(ext); if (!isInitialized) Init(); diff --git a/src/utility/comparators.cpp b/src/utility/comparators.cpp new file mode 100644 index 00000000..f4346399 --- /dev/null +++ b/src/utility/comparators.cpp @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright (C) 2008-2012 by Andrzej Rybczak * + * electricityispower@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "comparators.h" +#include "settings.h" + +int CaseInsensitiveStringComparison::operator()(const std::string &a, const std::string &b) +{ + const char *i = a.c_str(); + const char *j = b.c_str(); + if (Config.ignore_leading_the) + { + if (hasTheWord(a)) + i += 4; + if (hasTheWord(b)) + j += 4; + } + int dist; + while (!(dist = tolower(*i)-tolower(*j)) && *j) + ++i, ++j; + return dist; +} + +bool CaseInsensitiveSorting::operator()(const MPD::Item &a, const MPD::Item &b) +{ + bool result = false; + if (a.type == b.type) + { + switch (a.type) + { + case MPD::itDirectory: + result = cmp(getBasename(a.name), getBasename(b.name)) < 0; + break; + case MPD::itPlaylist: + result = cmp(a.name, b.name) < 0; + break; + case MPD::itSong: + switch (Config.browser_sort_mode) + { + case smName: + result = operator()(a.song, b.song); + break; + case smMTime: + result = a.song.getMTime() > b.song.getMTime(); + break; + case smCustomFormat: + result = cmp(a.song.toString(Config.browser_sort_format), b.song.toString(Config.browser_sort_format)) < 0; + break; + } + break; + default: // there is no other option, silence compiler + assert(false); + } + } + else + result = a.type < b.type; + return result; +} diff --git a/src/string_utilities.cpp b/src/utility/comparators.h similarity index 61% rename from src/string_utilities.cpp rename to src/utility/comparators.h index cbedba95..fa5b73b4 100644 --- a/src/string_utilities.cpp +++ b/src/utility/comparators.h @@ -18,25 +18,48 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include -#include "string_utilities.h" +#ifndef _UTILITY_COMPARATORS +#define _UTILITY_COMPARATORS -std::vector split(const std::string &s, const std::string &delimiter) +#include +#include "mpdpp.h" + +class CaseInsensitiveStringComparison { - if (delimiter.empty()) - return { s }; - std::vector result; - size_t i = 0, j = 0; - while (true) + bool hasTheWord(const std::string &s) { - i = j; - j = s.find(delimiter, i); - if (j == std::string::npos) - break; - else - result.push_back(s.substr(i, j-i)); - j += delimiter.length(); + return (s.length() > 3) + && (s[0] == 't' || s[0] == 'T') + && (s[1] == 'h' || s[1] == 'H') + && (s[2] == 'e' || s[2] == 'E') + && (s[3] == ' '); } - result.push_back(s.substr(i)); - return result; -} + +public: + int operator()(const std::string &a, const std::string &b); +}; + +class CaseInsensitiveSorting +{ + CaseInsensitiveStringComparison cmp; + +public: + bool operator()(const std::string &a, const std::string &b) + { + return cmp(a, b) < 0; + } + + bool operator()(const MPD::Song &a, const MPD::Song &b) + { + return cmp(a.getName(), b.getName()) < 0; + } + + template bool operator()(const std::pair &a, const std::pair &b) + { + return cmp(a.first, b.first) < 0; + } + + bool operator()(const MPD::Item &a, const MPD::Item &b); +}; + +#endif // _UTILITY_COMPARATORS \ No newline at end of file diff --git a/src/tolower.cpp b/src/utility/html.cpp similarity index 54% rename from src/tolower.cpp rename to src/utility/html.cpp index 8b24f099..73cefdb5 100644 --- a/src/tolower.cpp +++ b/src/utility/html.cpp @@ -18,18 +18,63 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#include -#include +#include "utility/html.h" +#include "utility/string.h" -#include "tolower.h" - -void ToLower(std::string &s) +std::string unescapeHtmlUtf8(const std::string &data) { - transform(s.begin(), s.end(), s.begin(), tolower); + std::string result; + for (size_t i = 0, j; i < data.length(); ++i) + { + if (data[i] == '&' && data[i+1] == '#' && (j = data.find(';', i)) != std::string::npos) + { + int n = atoi(&data.c_str()[i+2]); + if (n >= 0x800) + { + result += (0xe0 | ((n >> 12) & 0x0f)); + result += (0x80 | ((n >> 6) & 0x3f)); + result += (0x80 | (n & 0x3f)); + } + else if (n >= 0x80) + { + result += (0xc0 | ((n >> 6) & 0x1f)); + result += (0x80 | (n & 0x3f)); + } + else + result += n; + i = j; + } + else + result += data[i]; + } + return result; } -void ToLower(std::wstring &s) +void stripHtmlTags(std::string &s) { - transform(s.begin(), s.end(), s.begin(), towlower); -} - + bool erase = 0; + for (size_t i = s.find("<"); i != std::string::npos; i = s.find("<")) + { + size_t j = s.find(">", i)+1; + s.replace(i, j-i, ""); + } + replace(s, "'", "'"); + replace(s, "&", "&"); + replace(s, """, "\""); + replace(s, " ", " "); + for (size_t i = 0; i < s.length(); ++i) + { + if (erase) + { + s.erase(s.begin()+i); + erase = 0; + } + if (s[i] == 13) // ascii code for windows line ending, get rid of this shit + { + s[i] = '\n'; + erase = 1; + } + else if (s[i] == '\t') + s[i] = ' '; + } +} \ No newline at end of file diff --git a/src/tolower.h b/src/utility/html.h similarity index 90% rename from src/tolower.h rename to src/utility/html.h index a850499f..a8674360 100644 --- a/src/tolower.h +++ b/src/utility/html.h @@ -18,13 +18,13 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef HAVE_TOLOWER_H -#define HAVE_TOLOWER_H +#ifndef _UTILITY_HTML +#define _UTILITY_HTML #include -void ToLower(std::string &); -void ToLower(std::wstring &); +std::string unescapeHtmlUtf8(const std::string &s); -#endif +void stripHtmlTags(std::string &s); +#endif // _UTILITY_HTML diff --git a/src/utility/string.cpp b/src/utility/string.cpp new file mode 100644 index 00000000..e6219c75 --- /dev/null +++ b/src/utility/string.cpp @@ -0,0 +1,146 @@ +/*************************************************************************** + * Copyright (C) 2008-2012 by Andrzej Rybczak * + * electricityispower@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include +#include +#include +#include "utility/string.h" + +std::vector split(const std::string &s, const std::string &delimiter) +{ + if (delimiter.empty()) + return { s }; + std::vector result; + size_t i = 0, j = 0; + while (true) + { + i = j; + j = s.find(delimiter, i); + if (j == std::string::npos) + break; + else + result.push_back(s.substr(i, j-i)); + j += delimiter.length(); + } + result.push_back(s.substr(i)); + return result; +} + +void replace(std::string &s, const std::string &from, const std::string &to) +{ + for (size_t i = 0; (i = s.find(from, i)) != std::string::npos; i += to.length()) + s.replace(i, from.length(), to); +} + +void lowercase(std::string &s) +{ + std::transform(s.begin(), s.end(), s.begin(), tolower); +} + +void lowercase(std::wstring &ws) +{ + std::transform(ws.begin(), ws.end(), ws.begin(), towlower); +} + +void trim(std::string &s) +{ + if (s.empty()) + return; + + size_t b = 0; + size_t e = s.length()-1; + + while (s[e] == ' ' || s[e] == '\n' || s[e] == '\t') + --e; + ++e; + if (e != s.length()) + s.resize(e); + + while (s[b] == ' ' || s[b] == '\n' || s[e] == '\t') + ++b; + if (b != 0) + s = s.substr(b); +} + +std::string getBasename(const std::string &path) +{ + size_t slash = path.rfind("/"); + if (slash == std::string::npos) + return path; + else + return path.substr(slash+1); +} + +std::string getParentDirectory(const std::string &path) +{ + size_t slash = path.rfind('/'); + if (slash == std::string::npos) + return "/"; + else + return path.substr(0, slash); +} + +std::string getSharedDirectory(const std::string &dir1, const std::string &dir2) +{ + size_t i = 0; + size_t min_len = std::min(dir1.length(), dir2.length()); + while (i < min_len && !dir1.compare(i, 1, dir2, i, 1)) + ++i; + i = dir1.rfind("/", i); + if (i == std::string::npos) + return "/"; + else + return dir1.substr(0, i); +} + +std::string getEnclosedString(const std::string &s, char a, char b, size_t *pos) +{ + std::string result; + size_t i = s.find(a, pos ? *pos : 0); + if (i != std::string::npos) + { + ++i; + while (i < s.length() && s[i] != b) + { + if (s[i] == '\\' && i+1 < s.length() && s[i+1] == b) + result += s[++i]; + else + result += s[i]; + ++i; + } + // we want to set pos to char after b if possible + if (i < s.length()) + ++i; + } + if (pos != 0) + *pos = i; + return result; +} + +bool isInteger(const char *s) +{ + assert(s); + if (*s == '\0') + return false; + for (const char *it = s; *it != '\0'; ++it) + if (!isdigit(*it) && (it != s || *it != '-')) + return false; + return true; +} diff --git a/src/string_utilities.h b/src/utility/string.h similarity index 74% rename from src/string_utilities.h rename to src/utility/string.h index 6cee0329..a80d0c72 100644 --- a/src/string_utilities.h +++ b/src/utility/string.h @@ -18,12 +18,26 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ -#ifndef _STRING_UTILITIES -#define _STRING_UTILITIES +#ifndef _UTILITY_STRING +#define _UTILITY_STRING #include #include std::vector split(const std::string &s, const std::string &delimiter); +void replace(std::string &s, const std::string &from, const std::string &to); -#endif // _STRING_UTILITIES +void lowercase(std::string &s); +void lowercase(std::wstring &s); + +void trim(std::string &s); + +std::string getBasename(const std::string &path); +std::string getParentDirectory(const std::string &path); +std::string getSharedDirectory(const std::string &dir1, const std::string &dir2); + +std::string getEnclosedString(const std::string &s, char a, char b, size_t *pos); + +bool isInteger(const char *s); + +#endif // _UTILITY_STRING