wesnoth/src/strftime.cpp
Ignacio R. Morelle 390c93a191 Fix end iterator dereference with specific util::strftime() input
A format string with a trailing '%' character at the end would cause an
implementation detail to dereference the end iterator of a string. Since
the underlying storage is a null-terminated string, this would be
harmless in practice.

Found by cppcheck.
2015-07-06 22:48:05 -03:00

142 lines
3.8 KiB
C++

/*
Copyright (C) 2013 - 2015 by Andrius Silinskas <silinskas.andrius@gmail.com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
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.
See the COPYING file for more details.
*/
#include "strftime.hpp"
#include "gettext.hpp"
#include "language.hpp"
#include <string>
namespace {
const char *wday_abbr[] = {N_("Sun"), N_("Mon"), N_("Tue"), N_("Wed"),
N_("Thu"), N_("Fri"), N_("Sat")};
const char *wday_full[] = {N_("Sunday"), N_("Monday"), N_("Tuesday"),
N_("Wednesday"), N_("Thursday"), N_("Friday"), N_("Saturday")};
const char *mon_abbr[] = {N_("Jan"), N_("Feb"), N_("Mar"), N_("Apr"),
N_("abbrev^May"), N_("Jun"), N_("Jul"), N_("Aug"), N_("Sep"), N_("Oct"),
N_("Nov"), N_("Dec")};
const char *mon_full[] = {N_("January"), N_("February"), N_("March"),
N_("April"), N_("May"), N_("June"), N_("July"), N_("August"),
N_("September"), N_("October"), N_("November"), N_("December")};
}
static std::string reformat(const std::string& format, const std::tm* time,
bool locale_correct, bool ampm_supported)
{
if (locale_correct && ampm_supported) {
return format;
}
std::string new_format;
for (std::string::const_iterator it = format.begin(); it != format.end();
++it) {
if (*it == '%') {
++it;
if(it == format.end()) {
new_format += "%";
break;
}
bool unrecognized = false;
if (!locale_correct) {
switch (*it) {
case 'a': // abbreviated weekday name
new_format += (time->tm_wday < 0 || time->tm_wday > 6) ?
"?" : translation::sgettext(wday_abbr[time->tm_wday]);
continue;
case 'A': // full weekday name
new_format += (time->tm_wday < 0 || time->tm_wday > 6) ?
"?" : translation::sgettext(wday_full[time->tm_wday]);
continue;
case 'b': // abbreviated month name
case 'h':
new_format += (time->tm_mon < 0 || time->tm_mon > 11) ?
"?" : translation::sgettext(mon_abbr[time->tm_mon]);
continue;
case 'B': // full month name
new_format += (time->tm_mon < 0 || time->tm_mon > 11) ?
"?" : translation::sgettext(mon_full[time->tm_mon]);
continue;
case 'c': // date and time
new_format += reformat(_("%a %b %e %H:%M:%S %Y"), time,
locale_correct, ampm_supported);
continue;
case '+': // date and time
new_format += reformat(_("%a %d %b %Y %H:%M:%S %z"),
time, locale_correct, ampm_supported);
continue;
default:
unrecognized = true;
}
}
if (!ampm_supported || !locale_correct) {
switch (*it) {
case 'p': // AM or PM designation
new_format += (time->tm_hour < 12 ? _("AM") : _("PM"));
continue;
case 'P': // am or pm designation
new_format += (time->tm_hour < 12 ? _("am") : _("pm"));
continue;
default:
unrecognized = true;
}
}
// Unrecognized format specifiers should be left
// for std::strftime to deal with them.
if (unrecognized) {
new_format += "%";
}
}
new_format += *it;
}
return new_format;
}
static bool locale_supports_ampm(const std::tm* time)
{
const unsigned buffer_size = 16;
char time_buffer[buffer_size] = {0};
size_t ret = std::strftime(time_buffer, buffer_size, "%p", time);
if (ret == 0) {
return false;
}
return true;
}
namespace util {
size_t strftime(char* str, size_t count, const std::string& format,
const std::tm* time)
{
bool ampm_supported = locale_supports_ampm(time);
const std::string f = reformat(format, time, time_locale_correct(),
ampm_supported);
return std::strftime(str, count, f.c_str(), time);
}
} // end namespace util