1 //===-- string_utils.cpp ----------------------------------------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "string_utils.h" 10 #include "common.h" 11 12 #include <stdarg.h> 13 #include <string.h> 14 15 namespace scudo { 16 17 static int appendChar(char **Buffer, const char *BufferEnd, char C) { 18 if (*Buffer < BufferEnd) { 19 **Buffer = C; 20 (*Buffer)++; 21 } 22 return 1; 23 } 24 25 // Appends number in a given Base to buffer. If its length is less than 26 // |MinNumberLength|, it is padded with leading zeroes or spaces, depending 27 // on the value of |PadWithZero|. 28 static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue, 29 u8 Base, u8 MinNumberLength, bool PadWithZero, 30 bool Negative, bool Upper) { 31 constexpr uptr MaxLen = 30; 32 RAW_CHECK(Base == 10 || Base == 16); 33 RAW_CHECK(Base == 10 || !Negative); 34 RAW_CHECK(AbsoluteValue || !Negative); 35 RAW_CHECK(MinNumberLength < MaxLen); 36 int Res = 0; 37 if (Negative && MinNumberLength) 38 --MinNumberLength; 39 if (Negative && PadWithZero) 40 Res += appendChar(Buffer, BufferEnd, '-'); 41 uptr NumBuffer[MaxLen]; 42 int Pos = 0; 43 do { 44 RAW_CHECK_MSG(static_cast<uptr>(Pos) < MaxLen, 45 "appendNumber buffer overflow"); 46 NumBuffer[Pos++] = static_cast<uptr>(AbsoluteValue % Base); 47 AbsoluteValue /= Base; 48 } while (AbsoluteValue > 0); 49 if (Pos < MinNumberLength) { 50 memset(&NumBuffer[Pos], 0, 51 sizeof(NumBuffer[0]) * static_cast<uptr>(MinNumberLength - Pos)); 52 Pos = MinNumberLength; 53 } 54 RAW_CHECK(Pos > 0); 55 Pos--; 56 for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) { 57 char c = (PadWithZero || Pos == 0) ? '0' : ' '; 58 Res += appendChar(Buffer, BufferEnd, c); 59 } 60 if (Negative && !PadWithZero) 61 Res += appendChar(Buffer, BufferEnd, '-'); 62 for (; Pos >= 0; Pos--) { 63 char Digit = static_cast<char>(NumBuffer[Pos]); 64 Digit = static_cast<char>((Digit < 10) ? '0' + Digit 65 : (Upper ? 'A' : 'a') + Digit - 10); 66 Res += appendChar(Buffer, BufferEnd, Digit); 67 } 68 return Res; 69 } 70 71 static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num, 72 u8 Base, u8 MinNumberLength, bool PadWithZero, 73 bool Upper) { 74 return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength, 75 PadWithZero, /*Negative=*/false, Upper); 76 } 77 78 static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num, 79 u8 MinNumberLength, bool PadWithZero) { 80 const bool Negative = (Num < 0); 81 const u64 UnsignedNum = (Num == INT64_MIN) 82 ? static_cast<u64>(INT64_MAX) + 1 83 : static_cast<u64>(Negative ? -Num : Num); 84 return appendNumber(Buffer, BufferEnd, UnsignedNum, 10, MinNumberLength, 85 PadWithZero, Negative, /*Upper=*/false); 86 } 87 88 // Use the fact that explicitly requesting 0 Width (%0s) results in UB and 89 // interpret Width == 0 as "no Width requested": 90 // Width == 0 - no Width requested 91 // Width < 0 - left-justify S within and pad it to -Width chars, if necessary 92 // Width > 0 - right-justify S, not implemented yet 93 static int appendString(char **Buffer, const char *BufferEnd, int Width, 94 int MaxChars, const char *S) { 95 if (!S) 96 S = "<null>"; 97 int Res = 0; 98 for (; *S; S++) { 99 if (MaxChars >= 0 && Res >= MaxChars) 100 break; 101 Res += appendChar(Buffer, BufferEnd, *S); 102 } 103 // Only the left justified strings are supported. 104 while (Width < -Res) 105 Res += appendChar(Buffer, BufferEnd, ' '); 106 return Res; 107 } 108 109 static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) { 110 int Res = 0; 111 Res += appendString(Buffer, BufferEnd, 0, -1, "0x"); 112 Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16, 113 SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true, 114 /*Upper=*/false); 115 return Res; 116 } 117 118 static int formatString(char *Buffer, uptr BufferLength, const char *Format, 119 va_list Args) { 120 static const char *PrintfFormatsHelp = 121 "Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; " 122 "%[-]([0-9]*)?(\\.\\*)?s; %c\n"; 123 RAW_CHECK(Format); 124 RAW_CHECK(BufferLength > 0); 125 const char *BufferEnd = &Buffer[BufferLength - 1]; 126 const char *Cur = Format; 127 int Res = 0; 128 for (; *Cur; Cur++) { 129 if (*Cur != '%') { 130 Res += appendChar(&Buffer, BufferEnd, *Cur); 131 continue; 132 } 133 Cur++; 134 const bool LeftJustified = *Cur == '-'; 135 if (LeftJustified) 136 Cur++; 137 bool HaveWidth = (*Cur >= '0' && *Cur <= '9'); 138 const bool PadWithZero = (*Cur == '0'); 139 u8 Width = 0; 140 if (HaveWidth) { 141 while (*Cur >= '0' && *Cur <= '9') 142 Width = static_cast<u8>(Width * 10 + *Cur++ - '0'); 143 } 144 const bool HavePrecision = (Cur[0] == '.' && Cur[1] == '*'); 145 int Precision = -1; 146 if (HavePrecision) { 147 Cur += 2; 148 Precision = va_arg(Args, int); 149 } 150 const bool HaveZ = (*Cur == 'z'); 151 Cur += HaveZ; 152 const bool HaveLL = !HaveZ && (Cur[0] == 'l' && Cur[1] == 'l'); 153 Cur += HaveLL * 2; 154 s64 DVal; 155 u64 UVal; 156 const bool HaveLength = HaveZ || HaveLL; 157 const bool HaveFlags = HaveWidth || HaveLength; 158 // At the moment only %s supports precision and left-justification. 159 CHECK(!((Precision >= 0 || LeftJustified) && *Cur != 's')); 160 switch (*Cur) { 161 case 'd': { 162 DVal = HaveLL ? va_arg(Args, s64) 163 : HaveZ ? va_arg(Args, sptr) 164 : va_arg(Args, int); 165 Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero); 166 break; 167 } 168 case 'u': 169 case 'x': 170 case 'X': { 171 UVal = HaveLL ? va_arg(Args, u64) 172 : HaveZ ? va_arg(Args, uptr) 173 : va_arg(Args, unsigned); 174 const bool Upper = (*Cur == 'X'); 175 Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16, 176 Width, PadWithZero, Upper); 177 break; 178 } 179 case 'p': { 180 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 181 Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr)); 182 break; 183 } 184 case 's': { 185 RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp); 186 // Only left-justified Width is supported. 187 CHECK(!HaveWidth || LeftJustified); 188 Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width, 189 Precision, va_arg(Args, char *)); 190 break; 191 } 192 case 'c': { 193 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 194 Res += 195 appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int))); 196 break; 197 } 198 case '%': { 199 RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp); 200 Res += appendChar(&Buffer, BufferEnd, '%'); 201 break; 202 } 203 default: { 204 RAW_CHECK_MSG(false, PrintfFormatsHelp); 205 } 206 } 207 } 208 RAW_CHECK(Buffer <= BufferEnd); 209 appendChar(&Buffer, BufferEnd + 1, '\0'); 210 return Res; 211 } 212 213 int formatString(char *Buffer, uptr BufferLength, const char *Format, ...) { 214 va_list Args; 215 va_start(Args, Format); 216 int Res = formatString(Buffer, BufferLength, Format, Args); 217 va_end(Args); 218 return Res; 219 } 220 221 void ScopedString::append(const char *Format, va_list Args) { 222 va_list ArgsCopy; 223 va_copy(ArgsCopy, Args); 224 // formatString doesn't currently support a null buffer or zero buffer length, 225 // so in order to get the resulting formatted string length, we use a one-char 226 // buffer. 227 char C[1]; 228 const uptr AdditionalLength = 229 static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1; 230 const uptr Length = length(); 231 String.resize(Length + AdditionalLength); 232 const uptr FormattedLength = static_cast<uptr>(formatString( 233 String.data() + Length, String.size() - Length, Format, ArgsCopy)); 234 RAW_CHECK(data()[length()] == '\0'); 235 RAW_CHECK(FormattedLength + 1 == AdditionalLength); 236 va_end(ArgsCopy); 237 } 238 239 void ScopedString::append(const char *Format, ...) { 240 va_list Args; 241 va_start(Args, Format); 242 append(Format, Args); 243 va_end(Args); 244 } 245 246 void Printf(const char *Format, ...) { 247 va_list Args; 248 va_start(Args, Format); 249 ScopedString Msg; 250 Msg.append(Format, Args); 251 outputRaw(Msg.data()); 252 va_end(Args); 253 } 254 255 } // namespace scudo 256