16d46ebefSNico Weber //===-- string_utils.cpp ----------------------------------------*- C++ -*-===//
26d46ebefSNico Weber //
36d46ebefSNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
46d46ebefSNico Weber // See https://llvm.org/LICENSE.txt for license information.
56d46ebefSNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
66d46ebefSNico Weber //
76d46ebefSNico Weber //===----------------------------------------------------------------------===//
86d46ebefSNico Weber
96d46ebefSNico Weber #include "string_utils.h"
106d46ebefSNico Weber #include "common.h"
116d46ebefSNico Weber
126d46ebefSNico Weber #include <stdarg.h>
136d46ebefSNico Weber #include <string.h>
146d46ebefSNico Weber
156d46ebefSNico Weber namespace scudo {
166d46ebefSNico Weber
176d46ebefSNico Weber // Appends number in a given Base to buffer. If its length is less than
186d46ebefSNico Weber // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
196d46ebefSNico Weber // on the value of |PadWithZero|.
appendNumber(u64 AbsoluteValue,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Negative,bool Upper)20*1949f7d6SChristopher Ferris void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
21*1949f7d6SChristopher Ferris bool PadWithZero, bool Negative, bool Upper) {
226d46ebefSNico Weber constexpr uptr MaxLen = 30;
236d46ebefSNico Weber RAW_CHECK(Base == 10 || Base == 16);
246d46ebefSNico Weber RAW_CHECK(Base == 10 || !Negative);
256d46ebefSNico Weber RAW_CHECK(AbsoluteValue || !Negative);
266d46ebefSNico Weber RAW_CHECK(MinNumberLength < MaxLen);
276d46ebefSNico Weber if (Negative && MinNumberLength)
286d46ebefSNico Weber --MinNumberLength;
29*1949f7d6SChristopher Ferris if (Negative && PadWithZero) {
30*1949f7d6SChristopher Ferris String.push_back('-');
31*1949f7d6SChristopher Ferris }
326d46ebefSNico Weber uptr NumBuffer[MaxLen];
336d46ebefSNico Weber int Pos = 0;
346d46ebefSNico Weber do {
356d46ebefSNico Weber RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
366d46ebefSNico Weber "appendNumber buffer overflow");
376d46ebefSNico Weber NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
386d46ebefSNico Weber AbsoluteValue /= Base;
396d46ebefSNico Weber } while (AbsoluteValue > 0);
406d46ebefSNico Weber if (Pos < MinNumberLength) {
416d46ebefSNico Weber memset(&NumBuffer[Pos], 0,
426d46ebefSNico Weber sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
436d46ebefSNico Weber Pos = MinNumberLength;
446d46ebefSNico Weber }
456d46ebefSNico Weber RAW_CHECK(Pos > 0);
466d46ebefSNico Weber Pos--;
476d46ebefSNico Weber for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
486d46ebefSNico Weber char c = (PadWithZero || Pos == 0) ? '0' : ' ';
49*1949f7d6SChristopher Ferris String.push_back(c);
506d46ebefSNico Weber }
516d46ebefSNico Weber if (Negative && !PadWithZero)
52*1949f7d6SChristopher Ferris String.push_back('-');
536d46ebefSNico Weber for (; Pos >= 0; Pos--) {
546d46ebefSNico Weber char Digit = static_cast<char>(NumBuffer[Pos]);
556d46ebefSNico Weber Digit = static_cast<char>((Digit < 10) ? '0' + Digit
566d46ebefSNico Weber : (Upper ? 'A' : 'a') + Digit - 10);
57*1949f7d6SChristopher Ferris String.push_back(Digit);
586d46ebefSNico Weber }
596d46ebefSNico Weber }
606d46ebefSNico Weber
appendUnsigned(u64 Num,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Upper)61*1949f7d6SChristopher Ferris void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength,
62*1949f7d6SChristopher Ferris bool PadWithZero, bool Upper) {
63*1949f7d6SChristopher Ferris appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false,
64*1949f7d6SChristopher Ferris Upper);
656d46ebefSNico Weber }
666d46ebefSNico Weber
appendSignedDecimal(s64 Num,u8 MinNumberLength,bool PadWithZero)67*1949f7d6SChristopher Ferris void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength,
68*1949f7d6SChristopher Ferris bool PadWithZero) {
696d46ebefSNico Weber const bool Negative = (Num < 0);
702efc09c9SKostya Kortchinsky const u64 UnsignedNum = (Num == INT64_MIN)
712efc09c9SKostya Kortchinsky ? static_cast<u64>(INT64_MAX) + 1
722efc09c9SKostya Kortchinsky : static_cast<u64>(Negative ? -Num : Num);
73*1949f7d6SChristopher Ferris appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative,
74*1949f7d6SChristopher Ferris /*Upper=*/false);
756d46ebefSNico Weber }
766d46ebefSNico Weber
776d46ebefSNico Weber // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
786d46ebefSNico Weber // interpret Width == 0 as "no Width requested":
796d46ebefSNico Weber // Width == 0 - no Width requested
806d46ebefSNico Weber // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
816d46ebefSNico Weber // Width > 0 - right-justify S, not implemented yet
appendString(int Width,int MaxChars,const char * S)82*1949f7d6SChristopher Ferris void ScopedString::appendString(int Width, int MaxChars, const char *S) {
836d46ebefSNico Weber if (!S)
846d46ebefSNico Weber S = "<null>";
85*1949f7d6SChristopher Ferris int NumChars = 0;
866d46ebefSNico Weber for (; *S; S++) {
87*1949f7d6SChristopher Ferris if (MaxChars >= 0 && NumChars >= MaxChars)
886d46ebefSNico Weber break;
89*1949f7d6SChristopher Ferris String.push_back(*S);
90*1949f7d6SChristopher Ferris NumChars++;
916d46ebefSNico Weber }
92*1949f7d6SChristopher Ferris if (Width < 0) {
93*1949f7d6SChristopher Ferris // Only left justification supported.
94*1949f7d6SChristopher Ferris Width = -Width - NumChars;
95*1949f7d6SChristopher Ferris while (Width-- > 0)
96*1949f7d6SChristopher Ferris String.push_back(' ');
97*1949f7d6SChristopher Ferris }
986d46ebefSNico Weber }
996d46ebefSNico Weber
appendPointer(u64 ptr_value)100*1949f7d6SChristopher Ferris void ScopedString::appendPointer(u64 ptr_value) {
101*1949f7d6SChristopher Ferris appendString(0, -1, "0x");
102*1949f7d6SChristopher Ferris appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH,
103*1949f7d6SChristopher Ferris /*PadWithZero=*/true,
1046d46ebefSNico Weber /*Upper=*/false);
1056d46ebefSNico Weber }
1066d46ebefSNico Weber
vappend(const char * Format,va_list & Args)107*1949f7d6SChristopher Ferris void ScopedString::vappend(const char *Format, va_list &Args) {
108*1949f7d6SChristopher Ferris // Since the string contains the '\0' terminator, put our size before it
109*1949f7d6SChristopher Ferris // so that push_back calls work correctly.
110*1949f7d6SChristopher Ferris DCHECK(String.size() > 0);
111*1949f7d6SChristopher Ferris String.resize(String.size() - 1);
112*1949f7d6SChristopher Ferris
1136d46ebefSNico Weber static const char *PrintfFormatsHelp =
114*1949f7d6SChristopher Ferris "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
1156d46ebefSNico Weber "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
1166d46ebefSNico Weber RAW_CHECK(Format);
1176d46ebefSNico Weber const char *Cur = Format;
1186d46ebefSNico Weber for (; *Cur; Cur++) {
1196d46ebefSNico Weber if (*Cur != '%') {
120*1949f7d6SChristopher Ferris String.push_back(*Cur);
1216d46ebefSNico Weber continue;
1226d46ebefSNico Weber }
1236d46ebefSNico Weber Cur++;
1246d46ebefSNico Weber const bool LeftJustified = *Cur == '-';
1256d46ebefSNico Weber if (LeftJustified)
1266d46ebefSNico Weber Cur++;
1276d46ebefSNico Weber bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
1286d46ebefSNico Weber const bool PadWithZero = (*Cur == '0');
1296d46ebefSNico Weber u8 Width = 0;
1306d46ebefSNico Weber if (HaveWidth) {
1316d46ebefSNico Weber while (*Cur >= '0' && *Cur <= '9')
1326d46ebefSNico Weber Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
1336d46ebefSNico Weber }
1346d46ebefSNico Weber const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
1356d46ebefSNico Weber int Precision = -1;
1366d46ebefSNico Weber if (HavePrecision) {
1376d46ebefSNico Weber Cur += 2;
1386d46ebefSNico Weber Precision = va_arg(Args, int);
1396d46ebefSNico Weber }
1406d46ebefSNico Weber const bool HaveZ = (*Cur == 'z');
1416d46ebefSNico Weber Cur += HaveZ;
1426d46ebefSNico Weber const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
1436d46ebefSNico Weber Cur += HaveLL * 2;
1446d46ebefSNico Weber s64 DVal;
1456d46ebefSNico Weber u64 UVal;
1466d46ebefSNico Weber const bool HaveLength = HaveZ || HaveLL;
1476d46ebefSNico Weber const bool HaveFlags = HaveWidth || HaveLength;
1486d46ebefSNico Weber // At the moment only %s supports precision and left-justification.
1496d46ebefSNico Weber CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
1506d46ebefSNico Weber switch (*Cur) {
1516d46ebefSNico Weber case 'd': {
1526d46ebefSNico Weber DVal = HaveLL ? va_arg(Args, s64)
1532efc09c9SKostya Kortchinsky : HaveZ ? va_arg(Args, sptr)
1542efc09c9SKostya Kortchinsky : va_arg(Args, int);
155*1949f7d6SChristopher Ferris appendSignedDecimal(DVal, Width, PadWithZero);
1566d46ebefSNico Weber break;
1576d46ebefSNico Weber }
1586d46ebefSNico Weber case 'u':
1596d46ebefSNico Weber case 'x':
1606d46ebefSNico Weber case 'X': {
1616d46ebefSNico Weber UVal = HaveLL ? va_arg(Args, u64)
1622efc09c9SKostya Kortchinsky : HaveZ ? va_arg(Args, uptr)
1632efc09c9SKostya Kortchinsky : va_arg(Args, unsigned);
1646d46ebefSNico Weber const bool Upper = (*Cur == 'X');
165*1949f7d6SChristopher Ferris appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper);
1666d46ebefSNico Weber break;
1676d46ebefSNico Weber }
1686d46ebefSNico Weber case 'p': {
1696d46ebefSNico Weber RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
170*1949f7d6SChristopher Ferris appendPointer(va_arg(Args, uptr));
1716d46ebefSNico Weber break;
1726d46ebefSNico Weber }
1736d46ebefSNico Weber case 's': {
1746d46ebefSNico Weber RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
1756d46ebefSNico Weber // Only left-justified Width is supported.
1766d46ebefSNico Weber CHECK(!HaveWidth || LeftJustified);
177*1949f7d6SChristopher Ferris appendString(LeftJustified ? -Width : Width, Precision,
178*1949f7d6SChristopher Ferris va_arg(Args, char *));
1796d46ebefSNico Weber break;
1806d46ebefSNico Weber }
1816d46ebefSNico Weber case 'c': {
1826d46ebefSNico Weber RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
183*1949f7d6SChristopher Ferris String.push_back(static_cast<char>(va_arg(Args, int)));
1846d46ebefSNico Weber break;
1856d46ebefSNico Weber }
1861e36156aSChia-hung Duan // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
1871e36156aSChia-hung Duan // However, `-Wformat` doesn't know we have a different parser for those
1881e36156aSChia-hung Duan // placeholders and it keeps complaining the type mismatch on 64-bit
1891e36156aSChia-hung Duan // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to
1901e36156aSChia-hung Duan // silence the warning, we turn to use `PRId64`/`PRIu64` for printing
1911e36156aSChia-hung Duan // `s64`/`u64` and handle the `ld`/`lu` here.
1921e36156aSChia-hung Duan case 'l': {
1931e36156aSChia-hung Duan ++Cur;
1941e36156aSChia-hung Duan RAW_CHECK(*Cur == 'd' || *Cur == 'u');
1951e36156aSChia-hung Duan
1961e36156aSChia-hung Duan if (*Cur == 'd') {
1971e36156aSChia-hung Duan DVal = va_arg(Args, s64);
198*1949f7d6SChristopher Ferris appendSignedDecimal(DVal, Width, PadWithZero);
1991e36156aSChia-hung Duan } else {
2001e36156aSChia-hung Duan UVal = va_arg(Args, u64);
201*1949f7d6SChristopher Ferris appendUnsigned(UVal, 10, Width, PadWithZero, false);
2021e36156aSChia-hung Duan }
2031e36156aSChia-hung Duan
2041e36156aSChia-hung Duan break;
2051e36156aSChia-hung Duan }
2066d46ebefSNico Weber case '%': {
2076d46ebefSNico Weber RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
208*1949f7d6SChristopher Ferris String.push_back('%');
2096d46ebefSNico Weber break;
2106d46ebefSNico Weber }
2116d46ebefSNico Weber default: {
2126d46ebefSNico Weber RAW_CHECK_MSG(false, PrintfFormatsHelp);
2136d46ebefSNico Weber }
2146d46ebefSNico Weber }
2156d46ebefSNico Weber }
216*1949f7d6SChristopher Ferris String.push_back('\0');
217*1949f7d6SChristopher Ferris if (String.back() != '\0') {
218*1949f7d6SChristopher Ferris // String truncated, make sure the string is terminated properly.
219*1949f7d6SChristopher Ferris // This can happen if there is no more memory when trying to resize
220*1949f7d6SChristopher Ferris // the string.
221*1949f7d6SChristopher Ferris String.back() = '\0';
2226d46ebefSNico Weber }
2236d46ebefSNico Weber }
2246d46ebefSNico Weber
append(const char * Format,...)2256d46ebefSNico Weber void ScopedString::append(const char *Format, ...) {
2266d46ebefSNico Weber va_list Args;
2276d46ebefSNico Weber va_start(Args, Format);
228a27c416bSChristopher Ferris vappend(Format, Args);
2296d46ebefSNico Weber va_end(Args);
2306d46ebefSNico Weber }
2316d46ebefSNico Weber
Printf(const char * Format,...)2326d46ebefSNico Weber void Printf(const char *Format, ...) {
2336d46ebefSNico Weber va_list Args;
2346d46ebefSNico Weber va_start(Args, Format);
235868317b3SKostya Kortchinsky ScopedString Msg;
236a27c416bSChristopher Ferris Msg.vappend(Format, Args);
2376d46ebefSNico Weber outputRaw(Msg.data());
2386d46ebefSNico Weber va_end(Args);
2396d46ebefSNico Weber }
2406d46ebefSNico Weber
2416d46ebefSNico Weber } // namespace scudo
242