168d75effSDimitry Andric //===-- string_utils.cpp ----------------------------------------*- C++ -*-===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric 968d75effSDimitry Andric #include "string_utils.h" 1068d75effSDimitry Andric #include "common.h" 1168d75effSDimitry Andric 1268d75effSDimitry Andric #include <stdarg.h> 1368d75effSDimitry Andric #include <string.h> 1468d75effSDimitry Andric 1568d75effSDimitry Andric namespace scudo { 1668d75effSDimitry Andric 1768d75effSDimitry Andric // Appends number in a given Base to buffer. If its length is less than 1868d75effSDimitry Andric // |MinNumberLength|, it is padded with leading zeroes or spaces, depending 1968d75effSDimitry Andric // on the value of |PadWithZero|. 20*0fca6ea1SDimitry Andric void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength, 21*0fca6ea1SDimitry Andric bool PadWithZero, bool Negative, bool Upper) { 2268d75effSDimitry Andric constexpr uptr MaxLen = 30; 2368d75effSDimitry Andric RAW_CHECK(Base == 10 || Base == 16); 2468d75effSDimitry Andric RAW_CHECK(Base == 10 || !Negative); 2568d75effSDimitry Andric RAW_CHECK(AbsoluteValue || !Negative); 2668d75effSDimitry Andric RAW_CHECK(MinNumberLength < MaxLen); 2768d75effSDimitry Andric if (Negative && MinNumberLength) 2868d75effSDimitry Andric --MinNumberLength; 29*0fca6ea1SDimitry Andric if (Negative && PadWithZero) { 30*0fca6ea1SDimitry Andric String.push_back('-'); 31*0fca6ea1SDimitry Andric } 3268d75effSDimitry Andric uptr NumBuffer[MaxLen]; 3368d75effSDimitry Andric int Pos = 0; 3468d75effSDimitry Andric do { 3568d75effSDimitry Andric RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen, 3668d75effSDimitry Andric "appendNumber buffer overflow"); 3768d75effSDimitry Andric NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base); 3868d75effSDimitry Andric AbsoluteValue /= Base; 3968d75effSDimitry Andric } while (AbsoluteValue > 0); 4068d75effSDimitry Andric if (Pos < MinNumberLength) { 4168d75effSDimitry Andric memset(&NumBuffer[Pos], 0, 4268d75effSDimitry Andric sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos)); 4368d75effSDimitry Andric Pos = MinNumberLength; 4468d75effSDimitry Andric } 4568d75effSDimitry Andric RAW_CHECK(Pos > 0); 4668d75effSDimitry Andric Pos--; 4768d75effSDimitry Andric for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) { 4868d75effSDimitry Andric char c = (PadWithZero || Pos == 0) ? '0' : ' '; 49*0fca6ea1SDimitry Andric String.push_back(c); 5068d75effSDimitry Andric } 5168d75effSDimitry Andric if (Negative && !PadWithZero) 52*0fca6ea1SDimitry Andric String.push_back('-'); 5368d75effSDimitry Andric for (; Pos >= 0; Pos--) { 5468d75effSDimitry Andric char Digit = static_cast<char>(NumBuffer[Pos]); 5568d75effSDimitry Andric Digit = static_cast<char>((Digit < 10) ? '0' + Digit 5668d75effSDimitry Andric : (Upper ? 'A' : 'a') + Digit - 10); 57*0fca6ea1SDimitry Andric String.push_back(Digit); 5868d75effSDimitry Andric } 5968d75effSDimitry Andric } 6068d75effSDimitry Andric 61*0fca6ea1SDimitry Andric void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength, 62*0fca6ea1SDimitry Andric bool PadWithZero, bool Upper) { 63*0fca6ea1SDimitry Andric appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false, 64*0fca6ea1SDimitry Andric Upper); 6568d75effSDimitry Andric } 6668d75effSDimitry Andric 67*0fca6ea1SDimitry Andric void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength, 68*0fca6ea1SDimitry Andric bool PadWithZero) { 6968d75effSDimitry Andric const bool Negative = (Num < 0); 70e8d8bef9SDimitry Andric const u64 UnsignedNum = (Num == INT64_MIN) 71e8d8bef9SDimitry Andric ? static_cast<u64>(INT64_MAX) + 1 72e8d8bef9SDimitry Andric : static_cast<u64>(Negative ? -Num : Num); 73*0fca6ea1SDimitry Andric appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative, 74*0fca6ea1SDimitry Andric /*Upper=*/false); 7568d75effSDimitry Andric } 7668d75effSDimitry Andric 7768d75effSDimitry Andric // Use the fact that explicitly requesting 0 Width (%0s) results in UB and 7868d75effSDimitry Andric // interpret Width == 0 as "no Width requested": 7968d75effSDimitry Andric // Width == 0 - no Width requested 8068d75effSDimitry Andric // Width < 0 - left-justify S within and pad it to -Width chars, if necessary 8168d75effSDimitry Andric // Width > 0 - right-justify S, not implemented yet 82*0fca6ea1SDimitry Andric void ScopedString::appendString(int Width, int MaxChars, const char *S) { 8368d75effSDimitry Andric if (!S) 8468d75effSDimitry Andric S = "<null>"; 85*0fca6ea1SDimitry Andric int NumChars = 0; 8668d75effSDimitry Andric for (; *S; S++) { 87*0fca6ea1SDimitry Andric if (MaxChars >= 0 && NumChars >= MaxChars) 8868d75effSDimitry Andric break; 89*0fca6ea1SDimitry Andric String.push_back(*S); 90*0fca6ea1SDimitry Andric NumChars++; 9168d75effSDimitry Andric } 92*0fca6ea1SDimitry Andric if (Width < 0) { 93*0fca6ea1SDimitry Andric // Only left justification supported. 94*0fca6ea1SDimitry Andric Width = -Width - NumChars; 95*0fca6ea1SDimitry Andric while (Width-- > 0) 96*0fca6ea1SDimitry Andric String.push_back(' '); 97*0fca6ea1SDimitry Andric } 9868d75effSDimitry Andric } 9968d75effSDimitry Andric 100*0fca6ea1SDimitry Andric void ScopedString::appendPointer(u64 ptr_value) { 101*0fca6ea1SDimitry Andric appendString(0, -1, "0x"); 102*0fca6ea1SDimitry Andric appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH, 103*0fca6ea1SDimitry Andric /*PadWithZero=*/true, 10468d75effSDimitry Andric /*Upper=*/false); 10568d75effSDimitry Andric } 10668d75effSDimitry Andric 107*0fca6ea1SDimitry Andric void ScopedString::vappend(const char *Format, va_list &Args) { 108*0fca6ea1SDimitry Andric // Since the string contains the '\0' terminator, put our size before it 109*0fca6ea1SDimitry Andric // so that push_back calls work correctly. 110*0fca6ea1SDimitry Andric DCHECK(String.size() > 0); 111*0fca6ea1SDimitry Andric String.resize(String.size() - 1); 112*0fca6ea1SDimitry Andric 11368d75effSDimitry Andric static const char *PrintfFormatsHelp = 114*0fca6ea1SDimitry Andric "Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; " 11568d75effSDimitry Andric "%[-]([0-9]*)?(\\.\\*)?s; %c\n"; 11668d75effSDimitry Andric RAW_CHECK(Format); 11768d75effSDimitry Andric const char *Cur = Format; 11868d75effSDimitry Andric for (; *Cur; Cur++) { 11968d75effSDimitry Andric if (*Cur != '%') { 120*0fca6ea1SDimitry Andric String.push_back(*Cur); 12168d75effSDimitry Andric continue; 12268d75effSDimitry Andric } 12368d75effSDimitry Andric Cur++; 12468d75effSDimitry Andric const bool LeftJustified = *Cur == '-'; 12568d75effSDimitry Andric if (LeftJustified) 12668d75effSDimitry Andric Cur++; 12768d75effSDimitry Andric bool HaveWidth = (*Cur >= '0' && *Cur <= '9'); 12868d75effSDimitry Andric const bool PadWithZero = (*Cur == '0'); 12968d75effSDimitry Andric u8 Width = 0; 13068d75effSDimitry Andric if (HaveWidth) { 13168d75effSDimitry Andric while (*Cur >= '0' && *Cur <= '9') 13268d75effSDimitry Andric Width = static_cast<u8>(Width * 10 + *Cur++ - '0'); 13368d75effSDimitry Andric } 13468d75effSDimitry Andric const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*'); 13568d75effSDimitry Andric int Precision = -1; 13668d75effSDimitry Andric if (HavePrecision) { 13768d75effSDimitry Andric Cur += 2; 13868d75effSDimitry Andric Precision = va_arg(Args, int); 13968d75effSDimitry Andric } 14068d75effSDimitry Andric const bool HaveZ = (*Cur == 'z'); 14168d75effSDimitry Andric Cur += HaveZ; 14268d75effSDimitry Andric const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l'); 14368d75effSDimitry Andric Cur += HaveLL * 2; 14468d75effSDimitry Andric s64 DVal; 14568d75effSDimitry Andric u64 UVal; 14668d75effSDimitry Andric const bool HaveLength = HaveZ || HaveLL; 14768d75effSDimitry Andric const bool HaveFlags = HaveWidth || HaveLength; 14868d75effSDimitry Andric // At the moment only %s supports precision and left-justification. 14968d75effSDimitry Andric CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's')); 15068d75effSDimitry Andric switch (*Cur) { 15168d75effSDimitry Andric case 'd': { 15268d75effSDimitry Andric DVal = HaveLL ? va_arg(Args, s64) 153e8d8bef9SDimitry Andric : HaveZ ? va_arg(Args, sptr) 154e8d8bef9SDimitry Andric : va_arg(Args, int); 155*0fca6ea1SDimitry Andric appendSignedDecimal(DVal, Width, PadWithZero); 15668d75effSDimitry Andric break; 15768d75effSDimitry Andric } 15868d75effSDimitry Andric case 'u': 15968d75effSDimitry Andric case 'x': 16068d75effSDimitry Andric case 'X': { 16168d75effSDimitry Andric UVal = HaveLL ? va_arg(Args, u64) 162e8d8bef9SDimitry Andric : HaveZ ? va_arg(Args, uptr) 163e8d8bef9SDimitry Andric : va_arg(Args, unsigned); 16468d75effSDimitry Andric const bool Upper = (*Cur == 'X'); 165*0fca6ea1SDimitry Andric appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper); 16668d75effSDimitry Andric break; 16768d75effSDimitry Andric } 16868d75effSDimitry Andric case 'p': { 16968d75effSDimitry Andric RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 170*0fca6ea1SDimitry Andric appendPointer(va_arg(Args, uptr)); 17168d75effSDimitry Andric break; 17268d75effSDimitry Andric } 17368d75effSDimitry Andric case 's': { 17468d75effSDimitry Andric RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp); 17568d75effSDimitry Andric // Only left-justified Width is supported. 17668d75effSDimitry Andric CHECK(!HaveWidth || LeftJustified); 177*0fca6ea1SDimitry Andric appendString(LeftJustified ? -Width : Width, Precision, 178*0fca6ea1SDimitry Andric va_arg(Args, char *)); 17968d75effSDimitry Andric break; 18068d75effSDimitry Andric } 18168d75effSDimitry Andric case 'c': { 18268d75effSDimitry Andric RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 183*0fca6ea1SDimitry Andric String.push_back(static_cast<char>(va_arg(Args, int))); 18468d75effSDimitry Andric break; 18568d75effSDimitry Andric } 18606c3fb27SDimitry Andric // In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively. 18706c3fb27SDimitry Andric // However, `-Wformat` doesn't know we have a different parser for those 18806c3fb27SDimitry Andric // placeholders and it keeps complaining the type mismatch on 64-bit 18906c3fb27SDimitry Andric // platform which uses `ld`/`lu` for `s64`/`u64`. Therefore, in order to 19006c3fb27SDimitry Andric // silence the warning, we turn to use `PRId64`/`PRIu64` for printing 19106c3fb27SDimitry Andric // `s64`/`u64` and handle the `ld`/`lu` here. 19206c3fb27SDimitry Andric case 'l': { 19306c3fb27SDimitry Andric ++Cur; 19406c3fb27SDimitry Andric RAW_CHECK(*Cur == 'd' || *Cur == 'u'); 19506c3fb27SDimitry Andric 19606c3fb27SDimitry Andric if (*Cur == 'd') { 19706c3fb27SDimitry Andric DVal = va_arg(Args, s64); 198*0fca6ea1SDimitry Andric appendSignedDecimal(DVal, Width, PadWithZero); 19906c3fb27SDimitry Andric } else { 20006c3fb27SDimitry Andric UVal = va_arg(Args, u64); 201*0fca6ea1SDimitry Andric appendUnsigned(UVal, 10, Width, PadWithZero, false); 20206c3fb27SDimitry Andric } 20306c3fb27SDimitry Andric 20406c3fb27SDimitry Andric break; 20506c3fb27SDimitry Andric } 20668d75effSDimitry Andric case '%': { 20768d75effSDimitry Andric RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 208*0fca6ea1SDimitry Andric String.push_back('%'); 20968d75effSDimitry Andric break; 21068d75effSDimitry Andric } 21168d75effSDimitry Andric default: { 21268d75effSDimitry Andric RAW_CHECK_MSG(false, PrintfFormatsHelp); 21368d75effSDimitry Andric } 21468d75effSDimitry Andric } 21568d75effSDimitry Andric } 216*0fca6ea1SDimitry Andric String.push_back('\0'); 217*0fca6ea1SDimitry Andric if (String.back() != '\0') { 218*0fca6ea1SDimitry Andric // String truncated, make sure the string is terminated properly. 219*0fca6ea1SDimitry Andric // This can happen if there is no more memory when trying to resize 220*0fca6ea1SDimitry Andric // the string. 221*0fca6ea1SDimitry Andric String.back() = '\0'; 22268d75effSDimitry Andric } 22368d75effSDimitry Andric } 22468d75effSDimitry Andric 22568d75effSDimitry Andric void ScopedString::append(const char *Format, ...) { 22668d75effSDimitry Andric va_list Args; 22768d75effSDimitry Andric va_start(Args, Format); 22806c3fb27SDimitry Andric vappend(Format, Args); 22968d75effSDimitry Andric va_end(Args); 23068d75effSDimitry Andric } 23168d75effSDimitry Andric 23268d75effSDimitry Andric void Printf(const char *Format, ...) { 23368d75effSDimitry Andric va_list Args; 23468d75effSDimitry Andric va_start(Args, Format); 235fe6060f1SDimitry Andric ScopedString Msg; 23606c3fb27SDimitry Andric Msg.vappend(Format, Args); 23768d75effSDimitry Andric outputRaw(Msg.data()); 23868d75effSDimitry Andric va_end(Args); 23968d75effSDimitry Andric } 24068d75effSDimitry Andric 24168d75effSDimitry Andric } // namespace scudo 242