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