Skip to content

Commit d768703

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 96292fe commit d768703

File tree

1 file changed

+87
-93
lines changed

1 file changed

+87
-93
lines changed

gui/gui/src/TGNumberEntry.cxx

+87-93
Original file line numberDiff line numberDiff line change
@@ -233,19 +233,20 @@ 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+
static void CopyAndEliminateGarbage(char *dst,
237+
std::size_t dstCap,
238+
const char *src,
239+
TGNumberFormat::EStyle style,
240+
TGNumberFormat::EAttribute attr)
239241
{
240-
if (text == 0) {
241-
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);
242+
std::size_t dstIdx = 0;
243+
while (dstIdx < dstCap - 1 && src) {
244+
if (IsGoodChar(*src, style, attr)) {
245+
dst[dstIdx++] = *src;
246246
}
247+
++src;
247248
}
248-
return text;
249+
dst[dstIdx] = 0;
249250
}
250251

251252
////////////////////////////////////////////////////////////////////////////////
@@ -266,17 +267,9 @@ static Long_t IntStr(const char *text)
266267

267268
////////////////////////////////////////////////////////////////////////////////
268269

269-
static char *StrInt(char *text, Long_t i, Int_t digits)
270+
static char *StrInt(char *text, std::size_t textCap, Long_t i, Int_t digits)
270271
{
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);
272+
snprintf(text, textCap, "%0*li", digits + (i < 0), i);
280273
return text;
281274
}
282275

@@ -285,35 +278,45 @@ static char *StrInt(char *text, Long_t i, Int_t digits)
285278
static TString StringInt(Long_t i, Int_t digits)
286279
{
287280
char text[256];
288-
StrInt(text, i, digits);
281+
StrInt(text, sizeof(text), i, digits);
289282
return TString(text);
290283
}
291284

292285
////////////////////////////////////////////////////////////////////////////////
293286

294-
static char *RealToStr(char *text, const RealInfo_t & ri)
287+
static char *RealToStr(char *text, std::size_t textCap, const RealInfo_t & ri)
295288
{
296289
char *p = text;
297-
if (text == 0) {
298-
return 0;
290+
if (!text) {
291+
return nullptr;
299292
}
300-
strlcpy(p, "", 256);
293+
if (!textCap)
294+
return text;
295+
296+
const auto TextLen = [&p, text, textCap] () -> std::size_t {
297+
std::size_t curTextLen = p - text;
298+
if (curTextLen >= textCap)
299+
return 0;
300+
return textCap - curTextLen;
301+
};
302+
303+
strlcpy(p, "", textCap);
301304
if (ri.fSign < 0) {
302-
strlcpy(p, "-", 256);
305+
strlcpy(p, "-", textCap);
303306
p++;
304307
}
305-
StrInt(p, TMath::Abs(ri.fIntNum), 0);
308+
StrInt(p, TextLen(), TMath::Abs(ri.fIntNum), 0);
306309
p += strlen(p);
307310
if ((ri.fStyle == kRSFrac) || (ri.fStyle == kRSFracExpo)) {
308-
strlcpy(p, ".", 256-strlen(text));
311+
strlcpy(p, ".", TextLen());
309312
p++;
310-
StrInt(p, TMath::Abs(ri.fFracNum), ri.fFracDigits);
313+
StrInt(p, TextLen(), TMath::Abs(ri.fFracNum), ri.fFracDigits);
311314
p += strlen(p);
312315
}
313316
if ((ri.fStyle == kRSExpo) || (ri.fStyle == kRSFracExpo)) {
314-
strlcpy(p, "e", 256-strlen(text));
317+
strlcpy(p, "e", TextLen());
315318
p++;
316-
StrInt(p, ri.fExpoNum, 0);
319+
StrInt(p, TextLen(), ri.fExpoNum, 0);
317320
p += strlen(p);
318321
}
319322
return text;
@@ -436,30 +439,15 @@ static ULong_t HexStrToInt(const char *s)
436439

437440
////////////////////////////////////////////////////////////////////////////////
438441

439-
static char *IntToHexStr(char *text, ULong_t l)
442+
static char *IntToHexStr(char *text, std::size_t textCap, ULong_t l)
440443
{
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-
}
444+
snprintf(text, textCap, "%lX", l);
457445
return text;
458446
}
459447

460448
////////////////////////////////////////////////////////////////////////////////
461449

462-
static char *MIntToStr(char *text, Long_t l, Int_t digits)
450+
static char *MIntToStr(char *text, std::size_t textCap, Long_t l, Int_t digits)
463451
{
464452
TString s;
465453
Int_t base;
@@ -486,13 +474,13 @@ static char *MIntToStr(char *text, Long_t l, Int_t digits)
486474
if (l < 0) {
487475
s = "-" + s;
488476
}
489-
strlcpy(text, (const char *) s, 256);
477+
strlcpy(text, (const char *) s, textCap);
490478
return text;
491479
}
492480

493481
////////////////////////////////////////////////////////////////////////////////
494482

495-
static char *DIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
483+
static char *DIntToStr(char *text, std::size_t textCap, Long_t l, Bool_t Sec, char Del)
496484
{
497485
TString s;
498486
if (Sec) {
@@ -506,14 +494,14 @@ static char *DIntToStr(char *text, Long_t l, Bool_t Sec, char Del)
506494
if (l < 0) {
507495
s = "-" + s;
508496
}
509-
strlcpy(text, (const char *) s, 256);
497+
strlcpy(text, (const char *) s, textCap);
510498
return text;
511499
}
512500

513501
////////////////////////////////////////////////////////////////////////////////
514502
/// For kNESMinSecCent
515503

516-
static char *DIntToStr(char *text, Long_t l, char Del, char Del2)
504+
static char *DIntToStr(char *text, std::size_t textCap, Long_t l, char Del, char Del2)
517505
{
518506
TString s;
519507
s = StringInt(TMath::Abs(l) / 6000, 0) + Del +
@@ -522,7 +510,7 @@ static char *DIntToStr(char *text, Long_t l, char Del, char Del2)
522510
if (l < 0) {
523511
s = "-" + s;
524512
}
525-
strlcpy(text, (const char *) s, 256);
513+
strlcpy(text, (const char *) s, textCap);
526514
return text;
527515
}
528516

@@ -574,27 +562,33 @@ static Long_t GetSignificant(Long_t l, Int_t Max)
574562

575563
////////////////////////////////////////////////////////////////////////////////
576564

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

@@ -646,23 +640,23 @@ static Long_t TranslateToNum(const char *text,
646640
{
647641
char buf[256];
648642
strlcpy(buf, text, sizeof(buf));
649-
AppendFracZero(buf, 2);
643+
AppendFracZero(buf, sizeof(buf), 2);
650644
GetNumbers(buf, sign, n1, 12, n2, 2, n3, 0, ".,");
651645
return sign * (100 * n1 + GetSignificant(n2, 100));
652646
}
653647
case TGNumberFormat::kNESRealThree:
654648
{
655649
char buf[256];
656650
strlcpy(buf, text, sizeof(buf));
657-
AppendFracZero(buf, 3);
651+
AppendFracZero(buf, sizeof(buf), 3);
658652
GetNumbers(buf, sign, n1, 12, n2, 3, n3, 0, ".,");
659653
return sign * (1000 * n1 + GetSignificant(n2, 1000));
660654
}
661655
case TGNumberFormat::kNESRealFour:
662656
{
663657
char buf[256];
664658
strlcpy(buf, text, sizeof(buf));
665-
AppendFracZero(buf, 4);
659+
AppendFracZero(buf, sizeof(buf), 4);
666660
GetNumbers(buf, sign, n1, 12, n2, 4, n3, 0, ".,");
667661
return sign * (10000 * n1 + GetSignificant(n2, 10000));
668662
}
@@ -700,40 +694,41 @@ static Long_t TranslateToNum(const char *text,
700694

701695
////////////////////////////////////////////////////////////////////////////////
702696
/// Translate a number value to a string.
697+
/// `textCap` indicates the capacity of `text`.
703698

704-
static char *TranslateToStr(char *text, Long_t l,
699+
static char *TranslateToStr(char *text, std::size_t textCap, Long_t l,
705700
TGNumberFormat::EStyle style, const RealInfo_t & ri)
706701
{
707702
switch (style) {
708703
case TGNumberFormat::kNESInteger:
709-
return StrInt(text, l, 0);
704+
return StrInt(text, textCap, l, 0);
710705
case TGNumberFormat::kNESRealOne:
711-
return MIntToStr(text, l, 1);
706+
return MIntToStr(text, textCap, l, 1);
712707
case TGNumberFormat::kNESRealTwo:
713-
return MIntToStr(text, l, 2);
708+
return MIntToStr(text, textCap, l, 2);
714709
case TGNumberFormat::kNESRealThree:
715-
return MIntToStr(text, l, 3);
710+
return MIntToStr(text, textCap, l, 3);
716711
case TGNumberFormat::kNESRealFour:
717-
return MIntToStr(text, l, 4);
712+
return MIntToStr(text, textCap, l, 4);
718713
case TGNumberFormat::kNESReal:
719-
return RealToStr(text, ri);
714+
return RealToStr(text, textCap, ri);
720715
case TGNumberFormat::kNESDegree:
721-
return DIntToStr(text, l, kTRUE, '.');
716+
return DIntToStr(text, textCap, l, kTRUE, '.');
722717
case TGNumberFormat::kNESHourMinSec:
723-
return DIntToStr(text, l % (24 * 3600), kTRUE, ':');
718+
return DIntToStr(text, textCap, l % (24 * 3600), kTRUE, ':');
724719
case TGNumberFormat::kNESMinSec:
725-
return DIntToStr(text, l, kFALSE, ':');
720+
return DIntToStr(text, textCap, l, kFALSE, ':');
726721
case TGNumberFormat::kNESMinSecCent:
727-
return DIntToStr(text, l % (60 * 6000), ':', '.');
722+
return DIntToStr(text, textCap, l % (60 * 6000), ':', '.');
728723
case TGNumberFormat::kNESHourMin:
729-
return DIntToStr(text, l % (24 * 60), kFALSE, ':');
724+
return DIntToStr(text, textCap, l % (24 * 60), kFALSE, ':');
730725
case TGNumberFormat::kNESDayMYear:
731726
{
732727
TString date =
733728
StringInt(TMath::Abs(l) % 100, 0) + "/" +
734729
StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
735730
StringInt(TMath::Abs(l) / 10000, 0);
736-
strlcpy(text, (const char *) date, 256);
731+
strlcpy(text, (const char *) date, textCap);
737732
return text;
738733
}
739734
case TGNumberFormat::kNESMDayYear:
@@ -742,11 +737,11 @@ static char *TranslateToStr(char *text, Long_t l,
742737
StringInt((TMath::Abs(l) / 100) % 100, 0) + "/" +
743738
StringInt(TMath::Abs(l) % 100, 0) + "/" +
744739
StringInt(TMath::Abs(l) / 10000, 0);
745-
strlcpy(text, (const char *) date, 256);
740+
strlcpy(text, (const char *) date, textCap);
746741
return text;
747742
}
748743
case TGNumberFormat::kNESHex:
749-
return IntToHexStr(text, (ULong_t) l);
744+
return IntToHexStr(text, textCap, (ULong_t) l);
750745
}
751746
return 0;
752747
}
@@ -1200,9 +1195,9 @@ void TGNumberEntryField::SetIntNumber(Long_t val, Bool_t emit)
12001195
char text[256];
12011196
RealInfo_t ri;
12021197
if (fNumStyle == kNESReal) {
1203-
TranslateToStr(text, val, kNESInteger, ri);
1198+
TranslateToStr(text, sizeof(text), val, kNESInteger, ri);
12041199
} else {
1205-
TranslateToStr(text, val, fNumStyle, ri);
1200+
TranslateToStr(text, sizeof(text), val, fNumStyle, ri);
12061201
}
12071202
SetText(text, emit);
12081203
}
@@ -1265,8 +1260,7 @@ void TGNumberEntryField::SetHexNumber(ULong_t val, Bool_t emit)
12651260
void TGNumberEntryField::SetText(const char *text, Bool_t emit)
12661261
{
12671262
char buf[256];
1268-
strlcpy(buf, text, sizeof(buf));
1269-
EliminateGarbage(buf, fNumStyle, fNumAttr);
1263+
CopyAndEliminateGarbage(buf, sizeof(buf), text, fNumStyle, fNumAttr);
12701264
TGTextEntry::SetText(buf, emit);
12711265
fNeedsVerification = kFALSE;
12721266
}
@@ -1617,7 +1611,7 @@ void TGNumberEntryField::IncreaseNumber(EStepSize step,
16171611
SetIntNumber(l);
16181612
} else {
16191613
char buf[256];
1620-
RealToStr(buf, ri);
1614+
RealToStr(buf, sizeof(buf), ri);
16211615
SetText(buf);
16221616
}
16231617
}
@@ -2253,7 +2247,7 @@ void TGNumberEntry::SavePrimitive(std::ostream &out, Option_t *option /*= ""*/)
22532247
break;
22542248
case kNESHex: {
22552249
char hexstr[256];
2256-
IntToHexStr(hexstr, GetHexNumber());
2250+
IntToHexStr(hexstr, sizeof(hexstr), GetHexNumber());
22572251
out << "0x" << hexstr << "U, " << digits << ", " << WidgetId() << ",(TGNumberFormat::EStyle) " << GetNumStyle();
22582252
break;
22592253
}
@@ -2323,7 +2317,7 @@ void TGNumberEntryField::SavePrimitive(std::ostream &out, Option_t *option /*= "
23232317
case kNESMDayYear: out << yy << mm << dd << ",(TGNumberFormat::EStyle) " << GetNumStyle(); break;
23242318
case kNESHex: {
23252319
char hexstr[256];
2326-
IntToHexStr(hexstr, GetHexNumber());
2320+
IntToHexStr(hexstr, sizeof(hexstr), GetHexNumber());
23272321
out << "0x" << hexstr << "U, (TGNumberFormat::EStyle) " << GetNumStyle();
23282322
break;
23292323
}

0 commit comments

Comments
 (0)