Skip to content

Commit 0d7826f

Browse files
committed
[gui] Some fixes to TGNumberEntry
- fix some broken logic - simplify some functions - improve safety by adding bounds checks where appropriate
1 parent ba7619b commit 0d7826f

File tree

1 file changed

+90
-92
lines changed

1 file changed

+90
-92
lines changed

gui/gui/src/TGNumberEntry.cxx

Lines changed: 90 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,24 @@ static Bool_t IsGoodChar(char c, TGNumberFormat::EStyle style,
233233

234234
////////////////////////////////////////////////////////////////////////////////
235235

236-
static char *EliminateGarbage(char *text,
237-
TGNumberFormat::EStyle style,
238-
TGNumberFormat::EAttribute attr)
236+
/// Copies string `src` into `dst` (up to a length of `dstCap` including the null terminator)
237+
/// discarding every character that is not a "good character" for the given style and attribute.
238+
/// \return The length of the resulting string.
239+
static std::size_t CopyAndEliminateGarbage(char *dst, std::size_t dstCap, const char *src, TGNumberFormat::EStyle style,
240+
TGNumberFormat::EAttribute attr)
239241
{
240-
if (text == 0) {
242+
if (!src)
241243
return 0;
242-
}
243-
for (Int_t i = strlen(text) - 1; i >= 0; i--) {
244-
if (!IsGoodChar(text[i], style, attr)) {
245-
memmove(text + i, text + i + 1, strlen(text) - i);
244+
245+
std::size_t dstIdx = 0;
246+
while (dstIdx < dstCap - 1 && *src) {
247+
if (IsGoodChar(*src, style, attr)) {
248+
dst[dstIdx++] = *src;
246249
}
250+
++src;
247251
}
248-
return text;
252+
dst[dstIdx] = 0;
253+
return dstIdx;
249254
}
250255

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

267272
////////////////////////////////////////////////////////////////////////////////
268273

269-
static char *StrInt(char *text, Long_t i, Int_t digits)
274+
static char *StrInt(char *text, std::size_t textCap, Long_t i, Int_t digits)
270275
{
271-
snprintf(text, 250, "%li", TMath::Abs(i));
272-
TString s = text;
273-
while (digits > s.Length()) {
274-
s = "0" + s;
275-
}
276-
if (i < 0) {
277-
s = "-" + s;
278-
}
279-
strlcpy(text, (const char *) s, 250);
276+
snprintf(text, textCap, "%0*li", digits + (i < 0), i);
280277
return text;
281278
}
282279

@@ -285,35 +282,45 @@ static char *StrInt(char *text, Long_t i, Int_t digits)
285282
static TString StringInt(Long_t i, Int_t digits)
286283
{
287284
char text[256];
288-
StrInt(text, i, digits);
285+
StrInt(text, sizeof(text), i, digits);
289286
return TString(text);
290287
}
291288

292289
////////////////////////////////////////////////////////////////////////////////
293290

294-
static char *RealToStr(char *text, const RealInfo_t & ri)
291+
static char *RealToStr(char *text, std::size_t textCap, const RealInfo_t & ri)
295292
{
296293
char *p = text;
297-
if (text == 0) {
298-
return 0;
294+
if (!text) {
295+
return nullptr;
299296
}
300-
strlcpy(p, "", 256);
297+
if (!textCap)
298+
return text;
299+
300+
const auto TextLen = [&p, text, textCap] () -> std::size_t {
301+
std::size_t curTextLen = p - text;
302+
if (curTextLen >= textCap)
303+
return 0;
304+
return textCap - curTextLen;
305+
};
306+
307+
strlcpy(p, "", textCap);
301308
if (ri.fSign < 0) {
302-
strlcpy(p, "-", 256);
309+
strlcpy(p, "-", textCap);
303310
p++;
304311
}
305-
StrInt(p, TMath::Abs(ri.fIntNum), 0);
312+
StrInt(p, TextLen(), TMath::Abs(ri.fIntNum), 0);
306313
p += strlen(p);
307314
if ((ri.fStyle == kRSFrac) || (ri.fStyle == kRSFracExpo)) {
308-
strlcpy(p, ".", 256-strlen(text));
315+
strlcpy(p, ".", TextLen());
309316
p++;
310-
StrInt(p, TMath::Abs(ri.fFracNum), ri.fFracDigits);
317+
StrInt(p, TextLen(), TMath::Abs(ri.fFracNum), ri.fFracDigits);
311318
p += strlen(p);
312319
}
313320
if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
314-
strlcpy(p, "e", 256-strlen(text));
321+
strlcpy(p, "e", TextLen());
315322
p++;
316-
StrInt(p, ri.fExpoNum, 0);
323+
StrInt(p, TextLen(), ri.fExpoNum, 0);
317324
p += strlen(p);
318325
}
319326
return text;
@@ -436,30 +443,15 @@ static ULong_t HexStrToInt(const char *s)
436443

437444
////////////////////////////////////////////////////////////////////////////////
438445

439-
static char *IntToHexStr(char *text, ULong_t l)
446+
static char *IntToHexStr(char *text, std::size_t textCap, ULong_t l)
440447
{
441-
const char *const digits = "0123456789ABCDEF";
442-
char buf[64];
443-
char *p = buf + 62;
444-
// coverity[secure_coding]
445-
strcpy(p, "");
446-
while (l > 0) {
447-
*(--p) = digits[l % 16];
448-
l /= 16;
449-
}
450-
if (!p[0]) {
451-
// coverity[secure_coding]
452-
strcpy(text, "0");
453-
} else {
454-
// coverity[secure_coding]
455-
strcpy(text, p);
456-
}
448+
snprintf(text, textCap, "%lX", l);
457449
return text;
458450
}
459451

460452
////////////////////////////////////////////////////////////////////////////////
461453

462-
static char *MIntToStr(char *text, Long_t l, Int_t digits)
454+
static char *MIntToStr(char *text, std::size_t textCap, Long_t l, Int_t digits)
463455
{
464456
TString s;
465457
Int_t base;
@@ -486,13 +478,13 @@ static char *MIntToStr(char *text, Long_t l, Int_t digits)
486478
if (l < 0) {
487479
s = "-" + s;
488480
}
489-
strlcpy(text, (const char *) s, 256);
481+
strlcpy(text, (const char *) s, textCap);
490482
return text;
491483
}
492484

493485
////////////////////////////////////////////////////////////////////////////////
494486

495-
static char *DIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
487+
static char *DIntToStr(char *text, std::size_t textCap, Long_t l, Bool_t Sec, char Del)
496488
{
497489
TString s;
498490
if (Sec) {
@@ -506,14 +498,14 @@ static char *DIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
506498
if (l < 0) {
507499
s = "-" + s;
508500
}
509-
strlcpy(text, (const char *) s, 256);
501+
strlcpy(text, (const char *) s, textCap);
510502
return text;
511503
}
512504

513505
////////////////////////////////////////////////////////////////////////////////
514506
/// For kNESMinSecCent
515507

516-
static char *DIntToStr(char *text, Long_t l, char Del, char Del2)
508+
static char *DIntToStr(char *text, std::size_t textCap, Long_t l, char Del, char Del2)
517509
{
518510
TString s;
519511
s = StringInt(TMath::Abs(l) / 6000, 0) + Del +
@@ -522,7 +514,7 @@ static char *DIntToStr(char *text, Long_t l, char Del, char Del2)
522514
if (l < 0) {
523515
s = "-" + s;
524516
}
525-
strlcpy(text, (const char *) s, 256);
517+
strlcpy(text, (const char *) s, textCap);
526518
return text;
527519
}
528520

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

575567
////////////////////////////////////////////////////////////////////////////////
576568

577-
static void AppendFracZero(char *text, Int_t digits)
569+
/// Given a numeric string "xxx.yyy" or "xxx,yyy", makes sure that the fractional
570+
/// part (if present) always has at least `digits` digits, appending zeroes if needed.
571+
static void AppendFracZero(char *text, std::size_t textCap, Int_t digits)
578572
{
579-
char *p;
580573
Int_t found = 0;
581-
p = strchr(text, '.');
582-
if (p == 0) {
583-
p = strchr(text, ',');
574+
char *p = strrchr(text, '.');
575+
if (!p) {
576+
p = strrchr(text, ',');
584577
}
585-
if (p == 0) {
578+
if (!p) {
586579
return;
587580
}
588581
p++;
589-
for (UInt_t i = 0; i < strlen(p); i++) {
590-
if (isdigit(*p)) {
591-
found++;
592-
}
582+
auto pLen = strlen(p);
583+
for (UInt_t i = 0; i < pLen; i++) {
584+
// NOTE: converting to bool because isdigit doesn't technically necessarily return 0 or 1
585+
// (the specs mention it returns "a nonzero value" for positive cases).
586+
found += !!isdigit(p[i]);
593587
}
594-
while (found < digits) {
595-
// coverity[secure_coding]
596-
strcpy(p + strlen(p), "0");
597-
found++;
588+
auto pOff = p - text;
589+
assert(textCap>= pOff + pLen);
590+
auto remainingCap = textCap - pOff - pLen;
591+
const auto trailingZeroes = std::min<std::size_t>(std::max(0, digits - found), remainingCap - 1);
592+
if (trailingZeroes > 0) {
593+
memset(p + pLen, '0', trailingZeroes);
594+
// ensure the new string is null terminated
595+
p[pLen + trailingZeroes] = 0;
598596
}
599597
}
600598

@@ -646,23 +644,23 @@ static Long_t TranslateToNum(const char *text,
646644
{
647645
char buf[256];
648646
strlcpy(buf, text, sizeof(buf));
649-
AppendFracZero(buf, 2);
647+
AppendFracZero(buf, sizeof(buf), 2);
650648
GetNumbers(buf, sign, n1, 12, n2, 2, n3, 0, ".,");
651649
return sign * (100 * n1 + GetSignificant(n2, 100));
652650
}
653651
case TGNumberFormat::kNESRealThree:
654652
{
655653
char buf[256];
656654
strlcpy(buf, text, sizeof(buf));
657-
AppendFracZero(buf, 3);
655+
AppendFracZero(buf, sizeof(buf), 3);
658656
GetNumbers(buf, sign, n1, 12, n2, 3, n3, 0, ".,");
659657
return sign * (1000 * n1 + GetSignificant(n2, 1000));
660658
}
661659
case TGNumberFormat::kNESRealFour:
662660
{
663661
char buf[256];
664662
strlcpy(buf, text, sizeof(buf));
665-
AppendFracZero(buf, 4);
663+
AppendFracZero(buf, sizeof(buf), 4);
666664
GetNumbers(buf, sign, n1, 12, n2, 4, n3, 0, ".,");
667665
return sign * (10000 * n1 + GetSignificant(n2, 10000));
668666
}
@@ -700,40 +698,41 @@ static Long_t TranslateToNum(const char *text,
700698

701699
////////////////////////////////////////////////////////////////////////////////
702700
/// Translate a number value to a string.
701+
/// `textCap` indicates the capacity of `text`.
703702

704-
static char *TranslateToStr(char *text, Long_t l,
703+
static char *TranslateToStr(char *text, std::size_t textCap, Long_t l,
705704
TGNumberFormat::EStyle style, const RealInfo_t & ri)
706705
{
707706
switch (style) {
708707
case TGNumberFormat::kNESInteger:
709-
return StrInt(text, l, 0);
708+
return StrInt(text, textCap, l, 0);
710709
case TGNumberFormat::kNESRealOne:
711-
return MIntToStr(text, l, 1);
710+
return MIntToStr(text, textCap, l, 1);
712711
case TGNumberFormat::kNESRealTwo:
713-
return MIntToStr(text, l, 2);
712+
return MIntToStr(text, textCap, l, 2);
714713
case TGNumberFormat::kNESRealThree:
715-
return MIntToStr(text, l, 3);
714+
return MIntToStr(text, textCap, l, 3);
716715
case TGNumberFormat::kNESRealFour:
717-
return MIntToStr(text, l, 4);
716+
return MIntToStr(text, textCap, l, 4);
718717
case TGNumberFormat::kNESReal:
719-
return RealToStr(text, ri);
718+
return RealToStr(text, textCap, ri);
720719
case TGNumberFormat::kNESDegree:
721-
return DIntToStr(text, l, kTRUE, '.');
720+
return DIntToStr(text, textCap, l, kTRUE, '.');
722721
case TGNumberFormat::kNESHourMinSec:
723-
return DIntToStr(text, l % (24 * 3600), kTRUE, ':');
722+
return DIntToStr(text, textCap, l % (24 * 3600), kTRUE, ':');
724723
case TGNumberFormat::kNESMinSec:
725-
return DIntToStr(text, l, kFALSE, ':');
724+
return DIntToStr(text, textCap, l, kFALSE, ':');
726725
case TGNumberFormat::kNESMinSecCent:
727-
return DIntToStr(text, l % (60 * 6000), ':', '.');
726+
return DIntToStr(text, textCap, l % (60 * 6000), ':', '.');
728727
case TGNumberFormat::kNESHourMin:
729-
return DIntToStr(text, l % (24 * 60), kFALSE, ':');
728+
return DIntToStr(text, textCap, l % (24 * 60), kFALSE, ':');
730729
case TGNumberFormat::kNESDayMYear:
731730
{
732731
TString date =
733732
StringInt(TMath::Abs(l) % 100, 0) + "/" +
734733
StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
735734
StringInt(TMath::Abs(l) / 10000, 0);
736-
strlcpy(text, (const char *) date, 256);
735+
strlcpy(text, (const char *) date, textCap);
737736
return text;
738737
}
739738
case TGNumberFormat::kNESMDayYear:
@@ -742,11 +741,11 @@ static char *TranslateToStr(char *text, Long_t l,
742741
StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
743742
StringInt(TMath::Abs(l) % 100, 0) + "/" +
744743
StringInt(TMath::Abs(l) / 10000, 0);
745-
strlcpy(text, (const char *) date, 256);
744+
strlcpy(text, (const char *) date, textCap);
746745
return text;
747746
}
748747
case TGNumberFormat::kNESHex:
749-
return IntToHexStr(text, (ULong_t) l);
748+
return IntToHexStr(text, textCap, (ULong_t) l);
750749
}
751750
return 0;
752751
}
@@ -1200,9 +1199,9 @@ void TGNumberEntryField::SetIntNumber(Long_t val, Bool_t emit)
12001199
char text[256];
12011200
RealInfo_t ri;
12021201
if (fNumStyle == kNESReal) {
1203-
TranslateToStr(text, val, kNESInteger, ri);
1202+
TranslateToStr(text, sizeof(text), val, kNESInteger, ri);
12041203
} else {
1205-
TranslateToStr(text, val, fNumStyle, ri);
1204+
TranslateToStr(text, sizeof(text), val, fNumStyle, ri);
12061205
}
12071206
SetText(text, emit);
12081207
}
@@ -1265,8 +1264,7 @@ void TGNumberEntryField::SetHexNumber(ULong_t val, Bool_t emit)
12651264
void TGNumberEntryField::SetText(const char *text, Bool_t emit)
12661265
{
12671266
char buf[256];
1268-
strlcpy(buf, text, sizeof(buf));
1269-
EliminateGarbage(buf, fNumStyle, fNumAttr);
1267+
CopyAndEliminateGarbage(buf, sizeof(buf), text, fNumStyle, fNumAttr);
12701268
TGTextEntry::SetText(buf, emit);
12711269
fNeedsVerification = kFALSE;
12721270
}
@@ -1617,7 +1615,7 @@ void TGNumberEntryField::IncreaseNumber(EStepSize step,
16171615
SetIntNumber(l);
16181616
} else {
16191617
char buf[256];
1620-
RealToStr(buf, ri);
1618+
RealToStr(buf, sizeof(buf), ri);
16211619
SetText(buf);
16221620
}
16231621
}
@@ -2253,7 +2251,7 @@ void TGNumberEntry::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
22532251
break;
22542252
case kNESHex: {
22552253
char hexstr[256];
2256-
IntToHexStr(hexstr, GetHexNumber());
2254+
IntToHexStr(hexstr, sizeof(hexstr), GetHexNumber());
22572255
out << "0x" << hexstr << "U, " << digits << ", " << WidgetId() << ",(TGNumberFormat::EStyle) " << GetNumStyle();
22582256
break;
22592257
}
@@ -2323,7 +2321,7 @@ void TGNumberEntryField::SavePrimitive(std::ostream &out, Option_t *option /*= "
23232321
case kNESMDayYear: out << yy << mm << dd << ",(TGNumberFormat::EStyle) " << GetNumStyle(); break;
23242322
case kNESHex: {
23252323
char hexstr[256];
2326-
IntToHexStr(hexstr, GetHexNumber());
2324+
IntToHexStr(hexstr, sizeof(hexstr), GetHexNumber());
23272325
out << "0x" << hexstr << "U, (TGNumberFormat::EStyle) " << GetNumStyle();
23282326
break;
23292327
}

0 commit comments

Comments
 (0)