Skip to content

[gui] Some fixes to TGNumberEntry #18435

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 90 additions & 92 deletions gui/gui/src/TGNumberEntry.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -233,19 +233,24 @@ static Bool_t IsGoodChar(char c, TGNumberFormat::EStyle style,

////////////////////////////////////////////////////////////////////////////////

static char *EliminateGarbage(char *text,
TGNumberFormat::EStyle style,
TGNumberFormat::EAttribute attr)
/// Copies string `src` into `dst` (up to a length of `dstCap` including the null terminator)
/// discarding every character that is not a "good character" for the given style and attribute.
/// \return The length of the resulting string.
static std::size_t CopyAndEliminateGarbage(char *dst, std::size_t dstCap, const char *src, TGNumberFormat::EStyle style,
TGNumberFormat::EAttribute attr)
{
if (text == 0) {
if (!src)
return 0;
}
for (Int_t i = strlen(text) - 1; i >= 0; i--) {
if (!IsGoodChar(text[i], style, attr)) {
memmove(text + i, text + i + 1, strlen(text) - i);

std::size_t dstIdx = 0;
while (dstIdx < dstCap - 1 && *src) {
if (IsGoodChar(*src, style, attr)) {
dst[dstIdx++] = *src;
}
++src;
}
return text;
dst[dstIdx] = 0;
return dstIdx;
}

////////////////////////////////////////////////////////////////////////////////
Expand All @@ -266,17 +271,9 @@ static Long_t IntStr(const char *text)

////////////////////////////////////////////////////////////////////////////////

static char *StrInt(char *text, Long_t i, Int_t digits)
static char *StrInt(char *text, std::size_t textCap, Long_t i, Int_t digits)
{
snprintf(text, 250, "%li", TMath::Abs(i));
TString s = text;
while (digits > s.Length()) {
s = "0" + s;
}
if (i < 0) {
s = "-" + s;
}
strlcpy(text, (const char *) s, 250);
snprintf(text, textCap, "%0*li", digits + (i < 0), i);
return text;
}

Expand All @@ -285,35 +282,45 @@ static char *StrInt(char *text, Long_t i, Int_t digits)
static TString StringInt(Long_t i, Int_t digits)
{
char text[256];
StrInt(text, i, digits);
StrInt(text, sizeof(text), i, digits);
return TString(text);
}

////////////////////////////////////////////////////////////////////////////////

static char *RealToStr(char *text, const RealInfo_t & ri)
static char *RealToStr(char *text, std::size_t textCap, const RealInfo_t & ri)
{
char *p = text;
if (text == 0) {
return 0;
if (!text) {
return nullptr;
}
strlcpy(p, "", 256);
if (!textCap)
return text;

const auto TextLen = [&p, text, textCap] () -> std::size_t {
std::size_t curTextLen = p - text;
if (curTextLen >= textCap)
return 0;
return textCap - curTextLen;
};

strlcpy(p, "", textCap);
if (ri.fSign < 0) {
strlcpy(p, "-", 256);
strlcpy(p, "-", textCap);
p++;
}
StrInt(p, TMath::Abs(ri.fIntNum), 0);
StrInt(p, TextLen(), TMath::Abs(ri.fIntNum), 0);
p += strlen(p);
if ((ri.fStyle == kRSFrac) || (ri.fStyle == kRSFracExpo)) {
strlcpy(p, ".", 256-strlen(text));
strlcpy(p, ".", TextLen());
p++;
StrInt(p, TMath::Abs(ri.fFracNum), ri.fFracDigits);
StrInt(p, TextLen(), TMath::Abs(ri.fFracNum), ri.fFracDigits);
p += strlen(p);
}
if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
strlcpy(p, "e", 256-strlen(text));
strlcpy(p, "e", TextLen());
p++;
StrInt(p, ri.fExpoNum, 0);
StrInt(p, TextLen(), ri.fExpoNum, 0);
p += strlen(p);
}
return text;
Expand Down Expand Up @@ -436,30 +443,15 @@ static ULong_t HexStrToInt(const char *s)

////////////////////////////////////////////////////////////////////////////////

static char *IntToHexStr(char *text, ULong_t l)
static char *IntToHexStr(char *text, std::size_t textCap, ULong_t l)
{
const char *const digits = "0123456789ABCDEF";
char buf[64];
char *p = buf + 62;
// coverity[secure_coding]
strcpy(p, "");
while (l > 0) {
*(--p) = digits[l % 16];
l /= 16;
}
if (!p[0]) {
// coverity[secure_coding]
strcpy(text, "0");
} else {
// coverity[secure_coding]
strcpy(text, p);
}
snprintf(text, textCap, "%lX", l);
return text;
}

////////////////////////////////////////////////////////////////////////////////

static char *MIntToStr(char *text, Long_t l, Int_t digits)
static char *MIntToStr(char *text, std::size_t textCap, Long_t l, Int_t digits)
{
TString s;
Int_t base;
Expand All @@ -486,13 +478,13 @@ static char *MIntToStr(char *text, Long_t l, Int_t digits)
if (l < 0) {
s = "-" + s;
}
strlcpy(text, (const char *) s, 256);
strlcpy(text, (const char *) s, textCap);
return text;
}

////////////////////////////////////////////////////////////////////////////////

static char *DIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
static char *DIntToStr(char *text, std::size_t textCap, Long_t l, Bool_t Sec, char Del)
{
TString s;
if (Sec) {
Expand All @@ -506,14 +498,14 @@ static char *DIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
if (l < 0) {
s = "-" + s;
}
strlcpy(text, (const char *) s, 256);
strlcpy(text, (const char *) s, textCap);
return text;
}

////////////////////////////////////////////////////////////////////////////////
/// For kNESMinSecCent

static char *DIntToStr(char *text, Long_t l, char Del, char Del2)
static char *DIntToStr(char *text, std::size_t textCap, Long_t l, char Del, char Del2)
{
TString s;
s = StringInt(TMath::Abs(l) / 6000, 0) + Del +
Expand All @@ -522,7 +514,7 @@ static char *DIntToStr(char *text, Long_t l, char Del, char Del2)
if (l < 0) {
s = "-" + s;
}
strlcpy(text, (const char *) s, 256);
strlcpy(text, (const char *) s, textCap);
return text;
}

Expand Down Expand Up @@ -574,27 +566,33 @@ static Long_t GetSignificant(Long_t l, Int_t Max)

////////////////////////////////////////////////////////////////////////////////

static void AppendFracZero(char *text, Int_t digits)
/// Given a numeric string "xxx.yyy" or "xxx,yyy", makes sure that the fractional
/// part (if present) always has at least `digits` digits, appending zeroes if needed.
static void AppendFracZero(char *text, std::size_t textCap, Int_t digits)
{
char *p;
Int_t found = 0;
p = strchr(text, '.');
if (p == 0) {
p = strchr(text, ',');
char *p = strrchr(text, '.');
if (!p) {
p = strrchr(text, ',');
}
if (p == 0) {
if (!p) {
return;
}
p++;
for (UInt_t i = 0; i < strlen(p); i++) {
if (isdigit(*p)) {
found++;
}
auto pLen = strlen(p);
for (UInt_t i = 0; i < pLen; i++) {
// NOTE: converting to bool because isdigit doesn't technically necessarily return 0 or 1
// (the specs mention it returns "a nonzero value" for positive cases).
found += !!isdigit(p[i]);
}
while (found < digits) {
// coverity[secure_coding]
strcpy(p + strlen(p), "0");
found++;
auto pOff = p - text;
assert(textCap>= pOff + pLen);
auto remainingCap = textCap - pOff - pLen;
const auto trailingZeroes = std::min<std::size_t>(std::max(0, digits - found), remainingCap - 1);
if (trailingZeroes > 0) {
memset(p + pLen, '0', trailingZeroes);
// ensure the new string is null terminated
p[pLen + trailingZeroes] = 0;
}
}

Expand Down Expand Up @@ -646,23 +644,23 @@ static Long_t TranslateToNum(const char *text,
{
char buf[256];
strlcpy(buf, text, sizeof(buf));
AppendFracZero(buf, 2);
AppendFracZero(buf, sizeof(buf), 2);
GetNumbers(buf, sign, n1, 12, n2, 2, n3, 0, ".,");
return sign * (100 * n1 + GetSignificant(n2, 100));
}
case TGNumberFormat::kNESRealThree:
{
char buf[256];
strlcpy(buf, text, sizeof(buf));
AppendFracZero(buf, 3);
AppendFracZero(buf, sizeof(buf), 3);
GetNumbers(buf, sign, n1, 12, n2, 3, n3, 0, ".,");
return sign * (1000 * n1 + GetSignificant(n2, 1000));
}
case TGNumberFormat::kNESRealFour:
{
char buf[256];
strlcpy(buf, text, sizeof(buf));
AppendFracZero(buf, 4);
AppendFracZero(buf, sizeof(buf), 4);
GetNumbers(buf, sign, n1, 12, n2, 4, n3, 0, ".,");
return sign * (10000 * n1 + GetSignificant(n2, 10000));
}
Expand Down Expand Up @@ -700,40 +698,41 @@ static Long_t TranslateToNum(const char *text,

////////////////////////////////////////////////////////////////////////////////
/// Translate a number value to a string.
/// `textCap` indicates the capacity of `text`.

static char *TranslateToStr(char *text, Long_t l,
static char *TranslateToStr(char *text, std::size_t textCap, Long_t l,
TGNumberFormat::EStyle style, const RealInfo_t & ri)
{
switch (style) {
case TGNumberFormat::kNESInteger:
return StrInt(text, l, 0);
return StrInt(text, textCap, l, 0);
case TGNumberFormat::kNESRealOne:
return MIntToStr(text, l, 1);
return MIntToStr(text, textCap, l, 1);
case TGNumberFormat::kNESRealTwo:
return MIntToStr(text, l, 2);
return MIntToStr(text, textCap, l, 2);
case TGNumberFormat::kNESRealThree:
return MIntToStr(text, l, 3);
return MIntToStr(text, textCap, l, 3);
case TGNumberFormat::kNESRealFour:
return MIntToStr(text, l, 4);
return MIntToStr(text, textCap, l, 4);
case TGNumberFormat::kNESReal:
return RealToStr(text, ri);
return RealToStr(text, textCap, ri);
case TGNumberFormat::kNESDegree:
return DIntToStr(text, l, kTRUE, '.');
return DIntToStr(text, textCap, l, kTRUE, '.');
case TGNumberFormat::kNESHourMinSec:
return DIntToStr(text, l % (24 * 3600), kTRUE, ':');
return DIntToStr(text, textCap, l % (24 * 3600), kTRUE, ':');
case TGNumberFormat::kNESMinSec:
return DIntToStr(text, l, kFALSE, ':');
return DIntToStr(text, textCap, l, kFALSE, ':');
case TGNumberFormat::kNESMinSecCent:
return DIntToStr(text, l % (60 * 6000), ':', '.');
return DIntToStr(text, textCap, l % (60 * 6000), ':', '.');
case TGNumberFormat::kNESHourMin:
return DIntToStr(text, l % (24 * 60), kFALSE, ':');
return DIntToStr(text, textCap, l % (24 * 60), kFALSE, ':');
case TGNumberFormat::kNESDayMYear:
{
TString date =
StringInt(TMath::Abs(l) % 100, 0) + "/" +
StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
StringInt(TMath::Abs(l) / 10000, 0);
strlcpy(text, (const char *) date, 256);
strlcpy(text, (const char *) date, textCap);
return text;
}
case TGNumberFormat::kNESMDayYear:
Expand All @@ -742,11 +741,11 @@ static char *TranslateToStr(char *text, Long_t l,
StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
StringInt(TMath::Abs(l) % 100, 0) + "/" +
StringInt(TMath::Abs(l) / 10000, 0);
strlcpy(text, (const char *) date, 256);
strlcpy(text, (const char *) date, textCap);
return text;
}
case TGNumberFormat::kNESHex:
return IntToHexStr(text, (ULong_t) l);
return IntToHexStr(text, textCap, (ULong_t) l);
}
return 0;
}
Expand Down Expand Up @@ -1200,9 +1199,9 @@ void TGNumberEntryField::SetIntNumber(Long_t val, Bool_t emit)
char text[256];
RealInfo_t ri;
if (fNumStyle == kNESReal) {
TranslateToStr(text, val, kNESInteger, ri);
TranslateToStr(text, sizeof(text), val, kNESInteger, ri);
} else {
TranslateToStr(text, val, fNumStyle, ri);
TranslateToStr(text, sizeof(text), val, fNumStyle, ri);
}
SetText(text, emit);
}
Expand Down Expand Up @@ -1265,8 +1264,7 @@ void TGNumberEntryField::SetHexNumber(ULong_t val, Bool_t emit)
void TGNumberEntryField::SetText(const char *text, Bool_t emit)
{
char buf[256];
strlcpy(buf, text, sizeof(buf));
EliminateGarbage(buf, fNumStyle, fNumAttr);
CopyAndEliminateGarbage(buf, sizeof(buf), text, fNumStyle, fNumAttr);
TGTextEntry::SetText(buf, emit);
fNeedsVerification = kFALSE;
}
Expand Down Expand Up @@ -1617,7 +1615,7 @@ void TGNumberEntryField::IncreaseNumber(EStepSize step,
SetIntNumber(l);
} else {
char buf[256];
RealToStr(buf, ri);
RealToStr(buf, sizeof(buf), ri);
SetText(buf);
}
}
Expand Down Expand Up @@ -2253,7 +2251,7 @@ void TGNumberEntry::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
break;
case kNESHex: {
char hexstr[256];
IntToHexStr(hexstr, GetHexNumber());
IntToHexStr(hexstr, sizeof(hexstr), GetHexNumber());
out << "0x" << hexstr << "U, " << digits << ", " << WidgetId() << ",(TGNumberFormat::EStyle) " << GetNumStyle();
break;
}
Expand Down Expand Up @@ -2323,7 +2321,7 @@ void TGNumberEntryField::SavePrimitive(std::ostream &out, Option_t *option /*= "
case kNESMDayYear: out << yy << mm << dd << ",(TGNumberFormat::EStyle) " << GetNumStyle(); break;
case kNESHex: {
char hexstr[256];
IntToHexStr(hexstr, GetHexNumber());
IntToHexStr(hexstr, sizeof(hexstr), GetHexNumber());
out << "0x" << hexstr << "U, (TGNumberFormat::EStyle) " << GetNumStyle();
break;
}
Expand Down
Loading