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
appendChar(char ** Buffer,const char * BufferEnd,char C)176d46ebefSNico Weber static int appendChar(char **Buffer, const char *BufferEnd, char C) {
186d46ebefSNico Weber if (*Buffer < BufferEnd) {
196d46ebefSNico Weber **Buffer = C;
206d46ebefSNico Weber (*Buffer)++;
216d46ebefSNico Weber }
226d46ebefSNico Weber return 1;
236d46ebefSNico Weber }
246d46ebefSNico Weber
256d46ebefSNico Weber // Appends number in a given Base to buffer. If its length is less than
266d46ebefSNico Weber // |MinNumberLength|, it is padded with leading zeroes or spaces, depending
276d46ebefSNico Weber // on the value of |PadWithZero|.
appendNumber(char ** Buffer,const char * BufferEnd,u64 AbsoluteValue,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Negative,bool Upper)286d46ebefSNico Weber static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
296d46ebefSNico Weber u8 Base, u8 MinNumberLength, bool PadWithZero,
306d46ebefSNico Weber bool Negative, bool Upper) {
316d46ebefSNico Weber constexpr uptr MaxLen = 30;
326d46ebefSNico Weber RAW_CHECK(Base == 10 || Base == 16);
336d46ebefSNico Weber RAW_CHECK(Base == 10 || !Negative);
346d46ebefSNico Weber RAW_CHECK(AbsoluteValue || !Negative);
356d46ebefSNico Weber RAW_CHECK(MinNumberLength < MaxLen);
366d46ebefSNico Weber int Res = 0;
376d46ebefSNico Weber if (Negative && MinNumberLength)
386d46ebefSNico Weber --MinNumberLength;
396d46ebefSNico Weber if (Negative && PadWithZero)
406d46ebefSNico Weber Res += appendChar(Buffer, BufferEnd, '-');
416d46ebefSNico Weber uptr NumBuffer[MaxLen];
426d46ebefSNico Weber int Pos = 0;
436d46ebefSNico Weber do {
446d46ebefSNico Weber RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen,
456d46ebefSNico Weber "appendNumber buffer overflow");
466d46ebefSNico Weber NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base);
476d46ebefSNico Weber AbsoluteValue /= Base;
486d46ebefSNico Weber } while (AbsoluteValue > 0);
496d46ebefSNico Weber if (Pos < MinNumberLength) {
506d46ebefSNico Weber memset(&NumBuffer[Pos], 0,
516d46ebefSNico Weber sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos));
526d46ebefSNico Weber Pos = MinNumberLength;
536d46ebefSNico Weber }
546d46ebefSNico Weber RAW_CHECK(Pos > 0);
556d46ebefSNico Weber Pos--;
566d46ebefSNico Weber for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
576d46ebefSNico Weber char c = (PadWithZero || Pos == 0) ? '0' : ' ';
586d46ebefSNico Weber Res += appendChar(Buffer, BufferEnd, c);
596d46ebefSNico Weber }
606d46ebefSNico Weber if (Negative && !PadWithZero)
616d46ebefSNico Weber Res += appendChar(Buffer, BufferEnd, '-');
626d46ebefSNico Weber for (; Pos >= 0; Pos--) {
636d46ebefSNico Weber char Digit = static_cast<char>(NumBuffer[Pos]);
646d46ebefSNico Weber Digit = static_cast<char>((Digit < 10) ? '0' + Digit
656d46ebefSNico Weber : (Upper ? 'A' : 'a') + Digit - 10);
666d46ebefSNico Weber Res += appendChar(Buffer, BufferEnd, Digit);
676d46ebefSNico Weber }
686d46ebefSNico Weber return Res;
696d46ebefSNico Weber }
706d46ebefSNico Weber
appendUnsigned(char ** Buffer,const char * BufferEnd,u64 Num,u8 Base,u8 MinNumberLength,bool PadWithZero,bool Upper)716d46ebefSNico Weber static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
726d46ebefSNico Weber u8 Base, u8 MinNumberLength, bool PadWithZero,
736d46ebefSNico Weber bool Upper) {
746d46ebefSNico Weber return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
756d46ebefSNico Weber PadWithZero, /*Negative=*/false, Upper);
766d46ebefSNico Weber }
776d46ebefSNico Weber
appendSignedDecimal(char ** Buffer,const char * BufferEnd,s64 Num,u8 MinNumberLength,bool PadWithZero)786d46ebefSNico Weber static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
796d46ebefSNico Weber u8 MinNumberLength, bool PadWithZero) {
806d46ebefSNico Weber const bool Negative = (Num < 0);
812efc09c9SKostya Kortchinsky const u64 UnsignedNum = (Num == INT64_MIN)
822efc09c9SKostya Kortchinsky ? static_cast<u64>(INT64_MAX) + 1
832efc09c9SKostya Kortchinsky : static_cast<u64>(Negative ? -Num : Num);
842efc09c9SKostya Kortchinsky return appendNumber(Buffer, BufferEnd, UnsignedNum, 10, MinNumberLength,
852efc09c9SKostya Kortchinsky PadWithZero, Negative, /*Upper=*/false);
866d46ebefSNico Weber }
876d46ebefSNico Weber
886d46ebefSNico Weber // Use the fact that explicitly requesting 0 Width (%0s) results in UB and
896d46ebefSNico Weber // interpret Width == 0 as "no Width requested":
906d46ebefSNico Weber // Width == 0 - no Width requested
916d46ebefSNico Weber // Width < 0 - left-justify S within and pad it to -Width chars, if necessary
926d46ebefSNico Weber // Width > 0 - right-justify S, not implemented yet
appendString(char ** Buffer,const char * BufferEnd,int Width,int MaxChars,const char * S)936d46ebefSNico Weber static int appendString(char **Buffer, const char *BufferEnd, int Width,
946d46ebefSNico Weber int MaxChars, const char *S) {
956d46ebefSNico Weber if (!S)
966d46ebefSNico Weber S = "<null>";
976d46ebefSNico Weber int Res = 0;
986d46ebefSNico Weber for (; *S; S++) {
996d46ebefSNico Weber if (MaxChars >= 0 && Res >= MaxChars)
1006d46ebefSNico Weber break;
1016d46ebefSNico Weber Res += appendChar(Buffer, BufferEnd, *S);
1026d46ebefSNico Weber }
1036d46ebefSNico Weber // Only the left justified strings are supported.
1046d46ebefSNico Weber while (Width < -Res)
1056d46ebefSNico Weber Res += appendChar(Buffer, BufferEnd, ' ');
1066d46ebefSNico Weber return Res;
1076d46ebefSNico Weber }
1086d46ebefSNico Weber
appendPointer(char ** Buffer,const char * BufferEnd,u64 ptr_value)1096d46ebefSNico Weber static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
1106d46ebefSNico Weber int Res = 0;
1116d46ebefSNico Weber Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
1126d46ebefSNico Weber Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
1136d46ebefSNico Weber SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
1146d46ebefSNico Weber /*Upper=*/false);
1156d46ebefSNico Weber return Res;
1166d46ebefSNico Weber }
1176d46ebefSNico Weber
formatString(char * Buffer,uptr BufferLength,const char * Format,va_list Args)11818722834SKostya Kortchinsky static int formatString(char *Buffer, uptr BufferLength, const char *Format,
1196d46ebefSNico Weber va_list Args) {
1206d46ebefSNico Weber static const char *PrintfFormatsHelp =
1216d46ebefSNico Weber "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
1226d46ebefSNico Weber "%[-]([0-9]*)?(\\.\\*)?s; %c\n";
1236d46ebefSNico Weber RAW_CHECK(Format);
1246d46ebefSNico Weber RAW_CHECK(BufferLength > 0);
1256d46ebefSNico Weber const char *BufferEnd = &Buffer[BufferLength - 1];
1266d46ebefSNico Weber const char *Cur = Format;
1276d46ebefSNico Weber int Res = 0;
1286d46ebefSNico Weber for (; *Cur; Cur++) {
1296d46ebefSNico Weber if (*Cur != '%') {
1306d46ebefSNico Weber Res += appendChar(&Buffer, BufferEnd, *Cur);
1316d46ebefSNico Weber continue;
1326d46ebefSNico Weber }
1336d46ebefSNico Weber Cur++;
1346d46ebefSNico Weber const bool LeftJustified = *Cur == '-';
1356d46ebefSNico Weber if (LeftJustified)
1366d46ebefSNico Weber Cur++;
1376d46ebefSNico Weber bool HaveWidth = (*Cur >= '0' && *Cur <= '9');
1386d46ebefSNico Weber const bool PadWithZero = (*Cur == '0');
1396d46ebefSNico Weber u8 Width = 0;
1406d46ebefSNico Weber if (HaveWidth) {
1416d46ebefSNico Weber while (*Cur >= '0' && *Cur <= '9')
1426d46ebefSNico Weber Width = static_cast<u8>(Width * 10 + *Cur++ - '0');
1436d46ebefSNico Weber }
1446d46ebefSNico Weber const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*');
1456d46ebefSNico Weber int Precision = -1;
1466d46ebefSNico Weber if (HavePrecision) {
1476d46ebefSNico Weber Cur += 2;
1486d46ebefSNico Weber Precision = va_arg(Args, int);
1496d46ebefSNico Weber }
1506d46ebefSNico Weber const bool HaveZ = (*Cur == 'z');
1516d46ebefSNico Weber Cur += HaveZ;
1526d46ebefSNico Weber const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l');
1536d46ebefSNico Weber Cur += HaveLL * 2;
1546d46ebefSNico Weber s64 DVal;
1556d46ebefSNico Weber u64 UVal;
1566d46ebefSNico Weber const bool HaveLength = HaveZ || HaveLL;
1576d46ebefSNico Weber const bool HaveFlags = HaveWidth || HaveLength;
1586d46ebefSNico Weber // At the moment only %s supports precision and left-justification.
1596d46ebefSNico Weber CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's'));
1606d46ebefSNico Weber switch (*Cur) {
1616d46ebefSNico Weber case 'd': {
1626d46ebefSNico Weber DVal = HaveLL ? va_arg(Args, s64)
1632efc09c9SKostya Kortchinsky : HaveZ ? va_arg(Args, sptr)
1642efc09c9SKostya Kortchinsky : va_arg(Args, int);
1656d46ebefSNico Weber Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
1666d46ebefSNico Weber break;
1676d46ebefSNico Weber }
1686d46ebefSNico Weber case 'u':
1696d46ebefSNico Weber case 'x':
1706d46ebefSNico Weber case 'X': {
1716d46ebefSNico Weber UVal = HaveLL ? va_arg(Args, u64)
1722efc09c9SKostya Kortchinsky : HaveZ ? va_arg(Args, uptr)
1732efc09c9SKostya Kortchinsky : va_arg(Args, unsigned);
1746d46ebefSNico Weber const bool Upper = (*Cur == 'X');
1756d46ebefSNico Weber Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
1766d46ebefSNico Weber Width, PadWithZero, Upper);
1776d46ebefSNico Weber break;
1786d46ebefSNico Weber }
1796d46ebefSNico Weber case 'p': {
1806d46ebefSNico Weber RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
1816d46ebefSNico Weber Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
1826d46ebefSNico Weber break;
1836d46ebefSNico Weber }
1846d46ebefSNico Weber case 's': {
1856d46ebefSNico Weber RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
1866d46ebefSNico Weber // Only left-justified Width is supported.
1876d46ebefSNico Weber CHECK(!HaveWidth || LeftJustified);
1886d46ebefSNico Weber Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
1896d46ebefSNico Weber Precision, va_arg(Args, char *));
1906d46ebefSNico Weber break;
1916d46ebefSNico Weber }
1926d46ebefSNico Weber case 'c': {
1936d46ebefSNico Weber RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
1946d46ebefSNico Weber Res +=
1956d46ebefSNico Weber appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
1966d46ebefSNico Weber break;
1976d46ebefSNico Weber }
1986d46ebefSNico Weber case '%': {
1996d46ebefSNico Weber RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
2006d46ebefSNico Weber Res += appendChar(&Buffer, BufferEnd, '%');
2016d46ebefSNico Weber break;
2026d46ebefSNico Weber }
2036d46ebefSNico Weber default: {
2046d46ebefSNico Weber RAW_CHECK_MSG(false, PrintfFormatsHelp);
2056d46ebefSNico Weber }
2066d46ebefSNico Weber }
2076d46ebefSNico Weber }
2086d46ebefSNico Weber RAW_CHECK(Buffer <= BufferEnd);
2096d46ebefSNico Weber appendChar(&Buffer, BufferEnd + 1, '\0');
2106d46ebefSNico Weber return Res;
2116d46ebefSNico Weber }
2126d46ebefSNico Weber
formatString(char * Buffer,uptr BufferLength,const char * Format,...)21318722834SKostya Kortchinsky int formatString(char *Buffer, uptr BufferLength, const char *Format, ...) {
21418722834SKostya Kortchinsky va_list Args;
21518722834SKostya Kortchinsky va_start(Args, Format);
21618722834SKostya Kortchinsky int Res = formatString(Buffer, BufferLength, Format, Args);
21718722834SKostya Kortchinsky va_end(Args);
21818722834SKostya Kortchinsky return Res;
21918722834SKostya Kortchinsky }
22018722834SKostya Kortchinsky
append(const char * Format,va_list Args)2216d46ebefSNico Weber void ScopedString::append(const char *Format, va_list Args) {
222f7b1489fSKostya Kortchinsky va_list ArgsCopy;
223f7b1489fSKostya Kortchinsky va_copy(ArgsCopy, Args);
224f7b1489fSKostya Kortchinsky // formatString doesn't currently support a null buffer or zero buffer length,
225f7b1489fSKostya Kortchinsky // so in order to get the resulting formatted string length, we use a one-char
226f7b1489fSKostya Kortchinsky // buffer.
227f7b1489fSKostya Kortchinsky char C[1];
228f7b1489fSKostya Kortchinsky const uptr AdditionalLength =
229f7b1489fSKostya Kortchinsky static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1;
230*70b29213SVitaly Buka const uptr Length = length();
231f7b1489fSKostya Kortchinsky String.resize(Length + AdditionalLength);
232*70b29213SVitaly Buka const uptr FormattedLength = static_cast<uptr>(formatString(
233*70b29213SVitaly Buka String.data() + Length, String.size() - Length, Format, ArgsCopy));
234*70b29213SVitaly Buka RAW_CHECK(data()[length()] == '\0');
235*70b29213SVitaly Buka RAW_CHECK(FormattedLength + 1 == AdditionalLength);
2360a09c1ccSKostya Kortchinsky va_end(ArgsCopy);
2376d46ebefSNico Weber }
2386d46ebefSNico Weber
append(const char * Format,...)2396d46ebefSNico Weber void ScopedString::append(const char *Format, ...) {
2406d46ebefSNico Weber va_list Args;
2416d46ebefSNico Weber va_start(Args, Format);
2426d46ebefSNico Weber append(Format, Args);
2436d46ebefSNico Weber va_end(Args);
2446d46ebefSNico Weber }
2456d46ebefSNico Weber
Printf(const char * Format,...)2466d46ebefSNico Weber void Printf(const char *Format, ...) {
2476d46ebefSNico Weber va_list Args;
2486d46ebefSNico Weber va_start(Args, Format);
249868317b3SKostya Kortchinsky ScopedString Msg;
2506d46ebefSNico Weber Msg.append(Format, Args);
2516d46ebefSNico Weber outputRaw(Msg.data());
2526d46ebefSNico Weber va_end(Args);
2536d46ebefSNico Weber }
2546d46ebefSNico Weber
2556d46ebefSNico Weber } // namespace scudo
256