properly handle boost::bad_lexical_cast exceptions
This commit is contained in:
@@ -55,6 +55,7 @@ INCLUDES= $(all_includes)
|
||||
ncmpcpp_LDFLAGS = $(all_libraries)
|
||||
noinst_HEADERS = \
|
||||
utility/comparators.h \
|
||||
utility/conversion.h \
|
||||
utility/html.h \
|
||||
utility/string.h \
|
||||
utility/type_conversions.h \
|
||||
|
||||
101
src/actions.cpp
101
src/actions.cpp
@@ -36,6 +36,7 @@
|
||||
#include "helpers.h"
|
||||
#include "statusbar.h"
|
||||
#include "utility/comparators.h"
|
||||
#include "utility/conversion.h"
|
||||
|
||||
#include "bindings.h"
|
||||
#include "browser.h"
|
||||
@@ -1194,12 +1195,10 @@ void SetCrossfade::run()
|
||||
Statusbar::put() << "Set crossfade to: ";
|
||||
std::string crossfade = wFooter->getString(3);
|
||||
Statusbar::unlock();
|
||||
int cf = boost::lexical_cast<int>(crossfade);
|
||||
if (cf > 0)
|
||||
{
|
||||
Config.crossfade_time = cf;
|
||||
Mpd.SetCrossfade(cf);
|
||||
}
|
||||
int cf = fromString<unsigned>(crossfade);
|
||||
lowerBoundCheck(cf, 1);
|
||||
Config.crossfade_time = cf;
|
||||
Mpd.SetCrossfade(cf);
|
||||
}
|
||||
|
||||
void SetVolume::run()
|
||||
@@ -1210,12 +1209,10 @@ void SetVolume::run()
|
||||
Statusbar::put() << "Set volume to: ";
|
||||
std::string strvolume = wFooter->getString(3);
|
||||
Statusbar::unlock();
|
||||
int volume = boost::lexical_cast<int>(strvolume);
|
||||
if (volume >= 0 && volume <= 100)
|
||||
{
|
||||
Mpd.SetVolume(volume);
|
||||
Statusbar::msg("Volume set to %d%%", volume);
|
||||
}
|
||||
int volume = fromString<unsigned>(strvolume);
|
||||
boundsCheck(volume, 0, 100);
|
||||
Mpd.SetVolume(volume);
|
||||
Statusbar::msg("Volume set to %d%%", volume);
|
||||
}
|
||||
|
||||
bool EditSong::canBeRun() const
|
||||
@@ -1519,17 +1516,11 @@ void ToggleScreenLock::run()
|
||||
{
|
||||
Statusbar::lock();
|
||||
Statusbar::put() << "% of the locked screen's width to be reserved (20-80): ";
|
||||
std::string str_part = wFooter->getString(boost::lexical_cast<std::string>(Config.locked_screen_width_part*100));
|
||||
std::string strpart = wFooter->getString(boost::lexical_cast<std::string>(part));
|
||||
Statusbar::unlock();
|
||||
if (str_part.empty())
|
||||
return;
|
||||
part = boost::lexical_cast<int>(str_part);
|
||||
}
|
||||
if (part < 20 || part > 80)
|
||||
{
|
||||
Statusbar::msg("Number is out of range");
|
||||
return;
|
||||
part = fromString<unsigned>(strpart);
|
||||
}
|
||||
boundsCheck(part, 20, 80);
|
||||
Config.locked_screen_width_part = part/100.0;
|
||||
if (myScreen->lock())
|
||||
Statusbar::msg("Screen locked (with %d%% width)", part);
|
||||
@@ -1568,44 +1559,34 @@ void JumpToPositionInSong::run()
|
||||
const MPD::Song s = myPlaylist->nowPlayingSong();
|
||||
|
||||
Statusbar::lock();
|
||||
Statusbar::put() << "Position to go (in %/mm:ss/seconds(s)): ";
|
||||
std::string position = wFooter->getString();
|
||||
Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): ";
|
||||
std::string strpos = wFooter->getString();
|
||||
Statusbar::unlock();
|
||||
|
||||
if (position.empty())
|
||||
return;
|
||||
boost::regex rx;
|
||||
boost::smatch what;
|
||||
|
||||
unsigned newpos = 0;
|
||||
size_t special_pos;
|
||||
if ((special_pos = position.find(':')) != std::string::npos) // probably time in mm:ss
|
||||
if (boost::regex_match(strpos, what, rx.assign("([0-9]+):([0-9]{2})"))) // mm:ss
|
||||
{
|
||||
newpos = boost::lexical_cast<int>(position.substr(0, special_pos))*60
|
||||
+ boost::lexical_cast<int>(position.substr(special_pos +1));
|
||||
if (newpos <= myPlaylist->currentSongLength())
|
||||
Mpd.Seek(s.getPosition(), newpos);
|
||||
else
|
||||
Statusbar::msg("Out of bounds, 0:00-%s possible for mm:ss, %s given", s.getLength().c_str(), MPD::Song::ShowTime(newpos).c_str());
|
||||
int mins = fromString<int>(what[1]);
|
||||
int secs = fromString<int>(what[2]);
|
||||
boundsCheck(secs, 0, 60);
|
||||
Mpd.Seek(s.getPosition(), mins * 60 + secs);
|
||||
}
|
||||
else if ((special_pos = position.find('s')) != std::string::npos) // probably position in seconds
|
||||
else if (boost::regex_match(strpos, what, rx.assign("([0-9]+)s"))) // position in seconds
|
||||
{
|
||||
position.resize(special_pos);
|
||||
newpos = boost::lexical_cast<int>(position);
|
||||
if (newpos <= s.getDuration())
|
||||
Mpd.Seek(s.getPosition(), newpos);
|
||||
else
|
||||
Statusbar::msg("Out of bounds, 0-%d possible for seconds, %d given", s.getDuration(), newpos);
|
||||
int secs = fromString<int>(what[1]);
|
||||
Mpd.Seek(s.getPosition(), secs);
|
||||
}
|
||||
else if (boost::regex_match(strpos, what, rx.assign("([0-9]+)[%]{0,1}"))) // position in %
|
||||
{
|
||||
int percent = fromString<int>(what[1]);
|
||||
boundsCheck(percent, 0, 100);
|
||||
int secs = (percent * s.getDuration()) / 100.0;
|
||||
Mpd.Seek(s.getPosition(), secs);
|
||||
}
|
||||
else
|
||||
{
|
||||
special_pos = position.find('%');
|
||||
if (special_pos != std::string::npos)
|
||||
position.resize(special_pos);
|
||||
newpos = boost::lexical_cast<int>(position);
|
||||
if (newpos <= 100)
|
||||
Mpd.Seek(s.getPosition(), s.getDuration()*newpos/100.0);
|
||||
else
|
||||
Statusbar::msg("Out of bounds, 0-100 possible for %%, %d given", newpos);
|
||||
}
|
||||
Statusbar::msg("Invalid format ([m]:[ss], [s]s, [%%]%%, [%%] accepted)");
|
||||
}
|
||||
|
||||
bool ReverseSelection::canBeRun() const
|
||||
@@ -1968,8 +1949,9 @@ void AddRandomItems::run()
|
||||
|
||||
Statusbar::lock();
|
||||
Statusbar::put() << "Number of random " << tag_type_str << "s: ";
|
||||
size_t number = boost::lexical_cast<size_t>(wFooter->getString());
|
||||
std::string strnum = wFooter->getString();
|
||||
Statusbar::unlock();
|
||||
size_t number = fromString<size_t>(strnum);
|
||||
if (number && (answer == 's' ? Mpd.AddRandomSongs(number) : Mpd.AddRandomTag(tag_type, number)))
|
||||
Statusbar::msg("%zu random %s%s added to playlist", number, tag_type_str.c_str(), number == 1 ? "" : "s");
|
||||
}
|
||||
@@ -2093,14 +2075,8 @@ void SetSelectedItemsPriority::run()
|
||||
Statusbar::put() << "Set priority [0-255]: ";
|
||||
std::string strprio = wFooter->getString();
|
||||
Statusbar::unlock();
|
||||
if (!isInteger(strprio.c_str(), true))
|
||||
return;
|
||||
int prio = boost::lexical_cast<int>(strprio);
|
||||
if (prio < 0 || prio > 255)
|
||||
{
|
||||
Statusbar::msg("Number is out of range");
|
||||
return;
|
||||
}
|
||||
unsigned prio = fromString<unsigned>(strprio);
|
||||
boundsCheck(prio, 0u, 255u);
|
||||
myPlaylist->SetSelectedItemsPriority(prio);
|
||||
}
|
||||
|
||||
@@ -2117,9 +2093,8 @@ void FilterPlaylistOnPriorities::run()
|
||||
Statusbar::put() << "Show songs with priority higher than: ";
|
||||
std::string strprio = wFooter->getString();
|
||||
Statusbar::unlock();
|
||||
if (!isInteger(strprio.c_str(), false))
|
||||
return;
|
||||
unsigned prio = boost::lexical_cast<unsigned>(strprio);
|
||||
unsigned prio = fromString<unsigned>(strprio);
|
||||
boundsCheck(prio, 0u, 255u);
|
||||
myPlaylist->main().filter(myPlaylist->main().begin(), myPlaylist->main().end(),
|
||||
[prio](const NC::Menu<MPD::Song>::Item &s) {
|
||||
return s.value().getPrio() > prio;
|
||||
|
||||
@@ -47,6 +47,7 @@
|
||||
#include "statusbar.h"
|
||||
#include "visualizer.h"
|
||||
#include "title.h"
|
||||
#include "utility/conversion.h"
|
||||
|
||||
namespace boost {//
|
||||
|
||||
@@ -251,10 +252,21 @@ int main(int argc, char **argv)
|
||||
if (input == Key::noOp)
|
||||
continue;
|
||||
|
||||
auto k = Bindings.get(input);
|
||||
for (; k.first != k.second; ++k.first)
|
||||
if (k.first->execute())
|
||||
break;
|
||||
try
|
||||
{
|
||||
auto k = Bindings.get(input);
|
||||
for (; k.first != k.second; ++k.first)
|
||||
if (k.first->execute())
|
||||
break;
|
||||
}
|
||||
catch (ConversionError &e)
|
||||
{
|
||||
Statusbar::msg("Couldn't convert value '%s' to target type", e.value().c_str());
|
||||
}
|
||||
catch (OutOfBounds &e)
|
||||
{
|
||||
Statusbar::msg("%s", e.errorMessage().c_str());
|
||||
}
|
||||
|
||||
if (myScreen == myPlaylist)
|
||||
myPlaylist->EnableHighlighting();
|
||||
|
||||
12
src/tags.cpp
12
src/tags.cpp
@@ -131,8 +131,16 @@ void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag)
|
||||
tag->setTitle(ToWString(s.getTitle()));
|
||||
tag->setArtist(ToWString(s.getArtist()));
|
||||
tag->setAlbum(ToWString(s.getAlbum()));
|
||||
tag->setYear(boost::lexical_cast<TagLib::uint>(s.getDate()));
|
||||
tag->setTrack(boost::lexical_cast<TagLib::uint>(s.getTrack()));
|
||||
try {
|
||||
tag->setYear(boost::lexical_cast<TagLib::uint>(s.getDate()));
|
||||
} catch (boost::bad_lexical_cast &) {
|
||||
std::cerr << "writeCommonTags: couldn't write 'year' tag to '" << s.getURI() << "' as it's not a positive integer\n";
|
||||
}
|
||||
try {
|
||||
tag->setTrack(boost::lexical_cast<TagLib::uint>(s.getTrack()));
|
||||
} catch (boost::bad_lexical_cast &) {
|
||||
std::cerr << "writeCommonTags: couldn't write 'track' tag to '" << s.getURI() << "' as it's not a positive integer\n";
|
||||
}
|
||||
tag->setGenre(ToWString(s.getGenre()));
|
||||
tag->setComment(ToWString(s.getComment()));
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ void windowTitle(const std::string &) { }
|
||||
void windowTitle(const std::string &status)
|
||||
{
|
||||
if (strcmp(getenv("TERM"), "linux") && Config.set_window_title)
|
||||
std::cout << "\033]0;" << status << "\7";
|
||||
std::cout << "\033]0;" << status << "\7" << std::flush;
|
||||
}
|
||||
#endif // USE_PDCURSES
|
||||
|
||||
|
||||
117
src/utility/conversion.h
Normal file
117
src/utility/conversion.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/***************************************************************************
|
||||
* Copyright (C) 2008-2013 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 <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/type_traits/is_unsigned.hpp>
|
||||
|
||||
#include "gcc.h"
|
||||
|
||||
struct ConversionError
|
||||
{
|
||||
ConversionError(std::string source) : m_source_value(source) { }
|
||||
|
||||
const std::string &value() { return m_source_value; }
|
||||
|
||||
private:
|
||||
std::string m_target_type;
|
||||
std::string m_source_value;
|
||||
};
|
||||
|
||||
struct OutOfBounds
|
||||
{
|
||||
const std::string &errorMessage() { return m_error_message; }
|
||||
|
||||
template <typename Type>
|
||||
GNUC_NORETURN static void raise(const Type &value, const Type &lbound, const Type &ubound)
|
||||
{
|
||||
throw OutOfBounds((boost::format(
|
||||
"Value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
GNUC_NORETURN static void raiseLower(const Type &value, const Type &lbound)
|
||||
{
|
||||
throw OutOfBounds((boost::format(
|
||||
"Value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str());
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
GNUC_NORETURN static void raiseUpper(const Type &value, const Type &ubound)
|
||||
{
|
||||
throw OutOfBounds((boost::format(
|
||||
"Value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str());
|
||||
}
|
||||
|
||||
private:
|
||||
OutOfBounds(std::string msg) : m_error_message(msg) { }
|
||||
|
||||
std::string m_error_message;
|
||||
};
|
||||
|
||||
template <typename TargetT, bool isUnsigned>
|
||||
struct unsigned_checker
|
||||
{
|
||||
static void apply(const std::string &) { }
|
||||
};
|
||||
template <typename TargetT>
|
||||
struct unsigned_checker<TargetT, true>
|
||||
{
|
||||
static void apply(const std::string &s)
|
||||
{
|
||||
if (s[0] == '-')
|
||||
throw ConversionError(s);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TargetT>
|
||||
TargetT fromString(const std::string &source)
|
||||
{
|
||||
unsigned_checker<TargetT, boost::is_unsigned<TargetT>::value>::apply(source);
|
||||
try
|
||||
{
|
||||
return boost::lexical_cast<TargetT>(source);
|
||||
}
|
||||
catch (boost::bad_lexical_cast &)
|
||||
{
|
||||
throw ConversionError(source);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void boundsCheck(const Type &value, const Type &lbound, const Type &ubound)
|
||||
{
|
||||
if (value < lbound || value > ubound)
|
||||
OutOfBounds::raise(value, lbound, ubound);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void lowerBoundCheck(const Type &value, const Type &lbound)
|
||||
{
|
||||
if (value < lbound)
|
||||
OutOfBounds::raiseLower(value, lbound);
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
void upperBoundCheck(const Type &value, const Type &ubound)
|
||||
{
|
||||
if (value > ubound)
|
||||
OutOfBounds::raiseUpper(value, ubound);
|
||||
}
|
||||
@@ -23,17 +23,6 @@
|
||||
#include <algorithm>
|
||||
#include "utility/string.h"
|
||||
|
||||
bool isInteger(const char *s, bool accept_signed)
|
||||
{
|
||||
assert(s);
|
||||
if (*s == '\0')
|
||||
return false;
|
||||
for (const char *it = s; *it != '\0'; ++it)
|
||||
if (!(isdigit(*it) || (accept_signed && it == s && *it == '-')))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string getBasename(const std::string &path)
|
||||
{
|
||||
size_t slash = path.rfind("/");
|
||||
|
||||
@@ -31,8 +31,6 @@ template <size_t N> size_t const_strlen(const char (&)[N]) {
|
||||
return N-1;
|
||||
}
|
||||
|
||||
bool isInteger(const char *s, bool accept_signed);
|
||||
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user