1 //////////////////////////////////////////////////////////////////////////////// 2 // Copyright (c) 2005 by Andrei Alexandrescu 3 // Copyright (c) 2006 Peter K�mmel 4 // Permission to use, copy, modify, distribute, and sell this software for any 5 // purpose is hereby granted without fee, provided that the above copyright 6 // notice appear in all copies and that both that copyright notice and this 7 // permission notice appear in supporting documentation. 8 // The author makes no representations about the suitability of this software 9 // for any purpose. It is provided "as is" without express or implied 10 // warranty. 11 //////////////////////////////////////////////////////////////////////////////// 12 #ifndef LOKI_SAFEFORMAT_INC_ 13 #define LOKI_SAFEFORMAT_INC_ 14 15 // $Id: SafeFormat.h 911 2008-12-15 20:55:24Z syntheticpp $ 16 17 18 //////////////////////////////////////////////////////////////////////////////// 19 // This file contains definitions for SafePrintf. SafeScanf coming soon (the 20 // design is similar). 21 // See Alexandrescu, Andrei: Type-safe Formatting, C/C++ Users Journal, Aug 2005 22 //////////////////////////////////////////////////////////////////////////////// 23 24 #include <cstdio> 25 #include <climits> 26 #include <string> 27 #include <cstring> 28 #include <stdexcept> 29 #include <utility> 30 #include <cassert> 31 #include <locale> 32 #include <iostream> 33 34 #include <loki/LokiExport.h> 35 36 37 // long is 32 bit on 64-bit Windows! 38 // intptr_t used to get 64 bit on Win64 39 #if defined(_WIN32) || defined(_WIN64) 40 # define LOKI_SAFEFORMAT_SIGNED_LONG intptr_t 41 # define LOKI_SAFEFORMAT_UNSIGNED_LONG uintptr_t 42 #else 43 # define LOKI_SAFEFORMAT_SIGNED_LONG signed long 44 # define LOKI_SAFEFORMAT_UNSIGNED_LONG unsigned long 45 #endif 46 47 // Windows headers could have min/max defined 48 #ifdef max 49 # undef max 50 #endif 51 #ifdef min 52 # undef min 53 #endif 54 55 namespace Loki 56 { 57 58 // Crude writing method: writes straight to the file, unbuffered 59 // Must be combined with a buffer to work properly (and efficiently) 60 LOKI_EXPORT 61 void write(std::FILE* f, const char* from, const char* to); 62 63 // Write to an ostream 64 LOKI_EXPORT 65 void write(std::ostream& f, const char* from, const char* to); 66 67 // Write to a string 68 LOKI_EXPORT 69 void write(std::string& s, const char* from, const char* to); 70 71 // Write to a fixed-size buffer 72 template <class Char> write(std::pair<Char *,std::size_t> & s,const Char * from,const Char * to)73 void write(std::pair<Char*, std::size_t>& s, const Char* from, const Char* to) { 74 assert(from <= to); 75 if(from + s.second < to) 76 throw std::overflow_error(""); 77 // s.first: position one past the final copied element 78 s.first = std::copy(from, to, s.first); 79 // remaining buffer size 80 s.second -= to - from; 81 } 82 83 //////////////////////////////////////////////////////////////////////////////// 84 // PrintfState class template 85 // Holds the formatting state, and implements operator() to format stuff 86 // Todo: make sure errors are handled properly 87 //////////////////////////////////////////////////////////////////////////////// 88 89 template <class Device, class Char> 90 struct PrintfState { PrintfStatePrintfState91 PrintfState(Device dev, const Char * format) 92 : device_(dev) 93 , format_(format) 94 , width_(0) 95 , prec_(0) 96 , flags_(0) 97 , result_(0) { 98 Advance(); 99 } 100 ~PrintfStatePrintfState101 ~PrintfState() { 102 } 103 104 #define LOKI_PRINTF_STATE_FORWARD(type) \ 105 PrintfState& operator()(type par) {\ 106 return (*this)(static_cast< LOKI_SAFEFORMAT_UNSIGNED_LONG >(par)); \ 107 } 108 109 LOKI_PRINTF_STATE_FORWARD(bool) LOKI_PRINTF_STATE_FORWARDPrintfState110 LOKI_PRINTF_STATE_FORWARD(char) 111 LOKI_PRINTF_STATE_FORWARD(signed char) 112 LOKI_PRINTF_STATE_FORWARD(unsigned char) 113 LOKI_PRINTF_STATE_FORWARD(signed short) 114 LOKI_PRINTF_STATE_FORWARD(unsigned short) 115 LOKI_PRINTF_STATE_FORWARD(signed int) 116 LOKI_PRINTF_STATE_FORWARD(signed long) 117 #if (defined(_WIN32) || defined(_WIN64)) 118 LOKI_PRINTF_STATE_FORWARD(unsigned long) 119 #else 120 // on Windows already defined by uintptr_t 121 LOKI_PRINTF_STATE_FORWARD(unsigned int) 122 #endif 123 124 // Print (or gobble in case of the "*" specifier) an int 125 PrintfState& operator()(LOKI_SAFEFORMAT_UNSIGNED_LONG i) { 126 if (result_ == -1) return *this; // don't even bother 127 // % [flags] [width] [.prec] [modifier] type_char 128 // Fetch the flags 129 ReadFlags(); 130 if (*format_ == '*') { 131 // read the width and get out 132 SetWidth(static_cast<size_t>(i)); 133 ++format_; 134 return *this; 135 } 136 ReadWidth(); 137 // precision 138 if (*format_ == '.') { 139 // deal with precision 140 if (format_[1] == '*') { 141 // read the precision and get out 142 SetPrec(static_cast<size_t>(i)); 143 format_ += 2; 144 return *this; 145 } 146 ReadPrecision(); 147 } 148 ReadModifiers(); 149 // input size modifier 150 if (ForceShort()) { 151 // short int 152 const Char c = *format_; 153 if (c == 'x' || c == 'X' || c == 'u' || c == 'o') { 154 i = static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(static_cast<unsigned short>(i)); 155 } 156 } 157 FormatWithCurrentFlags(i); 158 return *this; 159 } 160 operatorPrintfState161 PrintfState& operator()(void* n) { 162 if (result_ == -1) return *this; // don't even bother 163 PrintUsing_snprintf(n,"p"); 164 return *this; 165 } 166 operatorPrintfState167 PrintfState& operator()(double n) { 168 if (result_ == -1) return *this; // don't even bother 169 PrintUsing_snprintf(n,"eEfgG"); 170 return *this; 171 } 172 operatorPrintfState173 PrintfState& operator()(long double n) { 174 if (result_ == -1) return *this; // don't even bother 175 PrintUsing_snprintf(n,"eEfgG"); 176 return *this; 177 } 178 179 // Store the number of characters printed so far operatorPrintfState180 PrintfState& operator()(int * pi) { 181 return StoreCountHelper(pi); 182 } 183 184 // Store the number of characters printed so far operatorPrintfState185 PrintfState& operator()(short * pi) { 186 return StoreCountHelper(pi); 187 } 188 189 // Store the number of characters printed so far operatorPrintfState190 PrintfState& operator()(long * pi) { 191 return StoreCountHelper(pi); 192 } 193 operatorPrintfState194 PrintfState& operator()(const std::string& stdstr) { 195 return operator()(stdstr.c_str()); 196 } 197 operatorPrintfState198 PrintfState& operator()(const char *const s) { 199 if (result_ == -1) return *this; 200 ReadLeaders(); 201 const char fmt = *format_; 202 if (fmt == 'p') { 203 FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(s)); 204 return *this; 205 } 206 if (fmt != 's') { 207 result_ = -1; 208 return *this; 209 } 210 const size_t len = std::min(std::strlen(s), prec_); 211 if (width_ > len) { 212 if (LeftJustify()) { 213 Write(s, s + len); 214 Fill(' ', width_ - len); 215 } else { 216 Fill(' ', width_ - len); 217 Write(s, s + len); 218 } 219 } else { 220 Write(s, s + len); 221 } 222 Next(); 223 return *this; 224 } 225 operatorPrintfState226 PrintfState& operator()(const void *const p) { 227 return (*this)(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(p)); 228 } 229 230 // read the result 231 operator int() const { 232 return static_cast<int>(result_); 233 } 234 235 private: 236 PrintfState& operator=(const PrintfState&); 237 template <typename T> StoreCountHelperPrintfState238 PrintfState& StoreCountHelper(T *const pi) { 239 if (result_ == -1) return *this; // don't even bother 240 ReadLeaders(); 241 const char fmt = *format_; 242 if (fmt == 'p') { // pointer 243 FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(pi)); 244 return *this; 245 } 246 if (fmt != 'n') { 247 result_ = -1; 248 return *this; 249 } 250 assert(pi != 0); 251 *pi = result_; 252 Next(); 253 return *this; 254 } 255 FormatWithCurrentFlagsPrintfState256 void FormatWithCurrentFlags(const LOKI_SAFEFORMAT_UNSIGNED_LONG i) { 257 // look at the format character 258 Char formatChar = *format_; 259 bool isSigned = formatChar == 'd' || formatChar == 'i'; 260 if (formatChar == 'p') { 261 formatChar = 'x'; // pointers go to hex 262 SetAlternateForm(); // printed with '0x' in front 263 isSigned = true; // that's what gcc does 264 } 265 if (!strchr("cdiuoxX", formatChar)) { 266 result_ = -1; 267 return; 268 } 269 Char buf[ 270 sizeof(LOKI_SAFEFORMAT_UNSIGNED_LONG) * 3 // digits 271 + 1 // sign or ' ' 272 + 2 // 0x or 0X 273 + 1]; // terminating zero 274 const Char *const bufEnd = buf + (sizeof(buf) / sizeof(Char)); 275 Char * bufLast = buf + (sizeof(buf) / sizeof(Char) - 1); 276 Char signChar = 0; 277 unsigned int base = 10; 278 279 if (formatChar == 'c') { 280 // Format only one character 281 // The 'fill with zeros' flag is ignored 282 ResetFillZeros(); 283 *bufLast = static_cast<char>(i); 284 } else { 285 // TODO: inefficient code, refactor 286 const bool negative = isSigned && static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i) < 0; 287 if (formatChar == 'o') base = 8; 288 else if (formatChar == 'x' || formatChar == 'X') base = 16; 289 bufLast = isSigned 290 ? RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i), bufLast, base, 291 formatChar == 'X') 292 : RenderWithoutSign(i, bufLast, base, 293 formatChar == 'X'); 294 // Add the sign 295 if (isSigned) { 296 negative ? signChar = '-' 297 : ShowSignAlways() ? signChar = '+' 298 : Blank() ? signChar = ' ' 299 : 0; 300 } 301 } 302 // precision 303 size_t 304 countDigits = bufEnd - bufLast, 305 countZeros = prec_ != size_t(-1) && countDigits < prec_ && 306 formatChar != 'c' 307 ? prec_ - countDigits 308 : 0, 309 countBase = base != 10 && AlternateForm() && i != 0 310 ? (base == 16 ? 2 : countZeros > 0 ? 0 : 1) 311 : 0, 312 countSign = (signChar != 0), 313 totalPrintable = countDigits + countZeros + countBase + countSign; 314 size_t countPadLeft = 0, countPadRight = 0; 315 if (width_ > totalPrintable) { 316 if (LeftJustify()) { 317 countPadRight = width_ - totalPrintable; 318 countPadLeft = 0; 319 } else { 320 countPadLeft = width_ - totalPrintable; 321 countPadRight = 0; 322 } 323 } 324 if (FillZeros() && prec_ == size_t(-1)) { 325 // pad with zeros and no precision - transfer padding to precision 326 countZeros = countPadLeft; 327 countPadLeft = 0; 328 } 329 // ok, all computed, ready to print to device 330 Fill(' ', countPadLeft); 331 if (signChar != 0) Write(&signChar, &signChar + 1); 332 if (countBase > 0) Fill('0', 1); 333 if (countBase == 2) Fill(formatChar, 1); 334 Fill('0', countZeros); 335 Write(bufLast, bufEnd); 336 Fill(' ', countPadRight); 337 // done, advance 338 Next(); 339 } 340 WritePrintfState341 void Write(const Char* b, const Char* e) { 342 if (result_ < 0) return; 343 const LOKI_SAFEFORMAT_SIGNED_LONG x = e - b; 344 write(device_, b, e); 345 result_ += x; 346 } 347 348 template <class Value> PrintUsing_snprintfPrintfState349 void PrintUsing_snprintf(Value n, const char* check_fmt_char) { 350 const Char *const fmt = format_ - 1; 351 assert(*fmt == '%'); 352 // enforce format string validity 353 ReadLeaders(); 354 // enforce format spec 355 if (!strchr(check_fmt_char, *format_)) { 356 result_ = -1; 357 return; 358 } 359 // format char validated, copy it to a temp and use legacy sprintf 360 ++format_; 361 Char fmtBuf[128], resultBuf[1024]; 362 if (format_ >= fmt + sizeof(fmtBuf) / sizeof(Char)) { 363 result_ = -1; 364 return; 365 } 366 memcpy(fmtBuf, fmt, (format_ - fmt) * sizeof(Char)); 367 fmtBuf[format_ - fmt] = 0; 368 369 const int stored = 370 #ifdef _MSC_VER 371 #if _MSC_VER < 1400 372 _snprintf 373 #else 374 _snprintf_s 375 #endif 376 #else 377 snprintf 378 #endif 379 (resultBuf, sizeof(resultBuf) / sizeof(Char), fmtBuf, n); 380 381 if (stored < 0) { 382 result_ = -1; 383 return; 384 } 385 Write(resultBuf, resultBuf + strlen(resultBuf)); 386 Advance(); // output stuff to the next format directive 387 } 388 FillPrintfState389 void Fill(const Char c, size_t n) { 390 for (; n > 0; --n) { 391 Write(&c, &c + 1); 392 } 393 } 394 RenderWithoutSignPrintfState395 Char* RenderWithoutSign(LOKI_SAFEFORMAT_UNSIGNED_LONG n, char* bufLast, 396 unsigned int base, bool uppercase) { 397 const Char hex1st = uppercase ? 'A' : 'a'; 398 for (;;) { 399 const LOKI_SAFEFORMAT_UNSIGNED_LONG next = n / base; 400 Char c = static_cast<Char>(n - next * base); 401 c = static_cast<Char>(c + (c <= 9 ? '0' : static_cast<Char>(hex1st - 10))); 402 *bufLast = c; 403 n = next; 404 if (n == 0) break; 405 --bufLast; 406 } 407 return bufLast; 408 } 409 RenderWithoutSignPrintfState410 char* RenderWithoutSign(LOKI_SAFEFORMAT_SIGNED_LONG n, char* bufLast, unsigned int base, 411 bool uppercase) { 412 if (n != LONG_MIN) { 413 return RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n < 0 ? -n : n), 414 bufLast, base, uppercase); 415 } 416 // annoying corner case 417 char* save = bufLast; 418 ++n; 419 bufLast = RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n), 420 bufLast, base, uppercase); 421 --(*save); 422 return bufLast; 423 } 424 NextPrintfState425 void Next() { 426 ++format_; 427 Advance(); 428 } 429 AdvancePrintfState430 void Advance() { 431 ResetAll(); 432 const Char* begin = format_; 433 for (;;) { 434 if (*format_ == '%') { 435 if (format_[1] != '%') { // It's a format specifier 436 Write(begin, format_); 437 ++format_; 438 break; 439 } 440 // It's a "%%" 441 Write(begin, ++format_); 442 begin = ++format_; 443 continue; 444 } 445 if (*format_ == 0) { 446 Write(begin, format_); 447 break; 448 } 449 ++format_; 450 } 451 } 452 ReadFlagsPrintfState453 void ReadFlags() { 454 for (;; ++format_) { 455 switch (*format_) { 456 case '-': SetLeftJustify(); break; 457 case '+': SetShowSignAlways(); break; 458 case ' ': SetBlank(); break; 459 case '#': SetAlternateForm(); break; 460 case '0': SetFillZeros(); break; 461 default: return; 462 } 463 } 464 } 465 ParseDecimalSizeTPrintfState466 void ParseDecimalSizeT(size_t& dest) { 467 if (!std::isdigit(*format_, std::locale())) return; 468 size_t r = 0; 469 do { 470 // TODO: inefficient - rewrite 471 r *= 10; 472 r += *format_ - '0'; 473 ++format_; 474 } while (std::isdigit(*format_, std::locale())); 475 dest = r; 476 } 477 ReadWidthPrintfState478 void ReadWidth() { 479 ParseDecimalSizeT(width_); 480 } 481 ReadPrecisionPrintfState482 void ReadPrecision() { 483 assert(*format_ == '.'); 484 ++format_; 485 ParseDecimalSizeT(prec_); 486 } 487 ReadModifiersPrintfState488 void ReadModifiers() { 489 switch (*format_) { 490 case 'h': SetForceShort(); ++format_; break; 491 case 'l': ++format_; break; 492 // more (C99 and platform-specific modifiers) to come 493 } 494 } 495 ReadLeadersPrintfState496 void ReadLeaders() { 497 ReadFlags(); 498 ReadWidth(); 499 if (*format_ == '.') ReadPrecision(); 500 ReadModifiers(); 501 } 502 503 enum { 504 leftJustify = 1, 505 showSignAlways = 2, 506 blank = 4, 507 alternateForm = 8, 508 fillZeros = 16, 509 forceShort = 32 510 }; 511 LeftJustifyPrintfState512 bool LeftJustify() const { return (flags_ & leftJustify) != 0; } ShowSignAlwaysPrintfState513 bool ShowSignAlways() const { return (flags_ & showSignAlways) != 0; } SetWidthPrintfState514 void SetWidth(size_t w) { width_ = w; } SetLeftJustifyPrintfState515 void SetLeftJustify() { flags_ |= leftJustify; } SetShowSignAlwaysPrintfState516 void SetShowSignAlways() { flags_ |= showSignAlways; } BlankPrintfState517 bool Blank() const { return (flags_ & blank) != 0; } AlternateFormPrintfState518 bool AlternateForm() const { return (flags_ & alternateForm) != 0; } FillZerosPrintfState519 bool FillZeros() const { return (flags_ & fillZeros) != 0; } ForceShortPrintfState520 bool ForceShort() const { return (flags_ & forceShort) != 0; } 521 SetPrecPrintfState522 void SetPrec(size_t p) { prec_ = p; } SetBlankPrintfState523 void SetBlank() { flags_ |= blank; } SetAlternateFormPrintfState524 void SetAlternateForm() { flags_ |= alternateForm; } SetFillZerosPrintfState525 void SetFillZeros() { flags_ |= fillZeros; } ResetFillZerosPrintfState526 void ResetFillZeros() { flags_ &= ~fillZeros; } SetForceShortPrintfState527 void SetForceShort() { flags_ |= forceShort; } 528 ResetAllPrintfState529 void ResetAll() { 530 assert(result_ != EOF); 531 width_ = 0; 532 prec_ = size_t(-1); 533 flags_ = 0; 534 } 535 536 // state 537 Device device_; 538 const Char* format_; 539 size_t width_; 540 size_t prec_; 541 unsigned int flags_; 542 LOKI_SAFEFORMAT_SIGNED_LONG result_; 543 }; 544 545 LOKI_EXPORT 546 PrintfState<std::FILE*, char> Printf(const char* format); 547 548 LOKI_EXPORT 549 PrintfState<std::FILE*, char> Printf(const std::string& format); 550 551 LOKI_EXPORT 552 PrintfState<std::FILE*, char> FPrintf(std::FILE* f, const char* format); 553 554 LOKI_EXPORT 555 PrintfState<std::FILE*, char> FPrintf(std::FILE* f, const std::string& format); 556 557 LOKI_EXPORT 558 PrintfState<std::ostream&, char> FPrintf(std::ostream& f, const char* format); 559 560 LOKI_EXPORT 561 PrintfState<std::ostream&, char> FPrintf(std::ostream& f, const std::string& format); 562 563 LOKI_EXPORT 564 PrintfState<std::string&, char> SPrintf(std::string& s, const char* format); 565 566 LOKI_EXPORT 567 PrintfState<std::string&, char> SPrintf(std::string& s, const std::string& format); 568 569 template <class T, class Char> XPrintf(T & device,const Char * format)570 PrintfState<T&, Char> XPrintf(T& device, const Char* format) { 571 return PrintfState<T&, Char>(device, format); 572 } 573 574 template <class T> XPrintf(T & device,const std::string & format)575 PrintfState<T&, char> XPrintf(T& device, const std::string& format) { 576 return PrintfState<T&, char>(device, format.c_str()); 577 } 578 579 template <class Char, std::size_t N> 580 PrintfState<std::pair<Char*, std::size_t>, Char> BufPrintf(Char (& buf)[N],const Char * format)581 BufPrintf(Char (&buf)[N], const Char* format) { 582 std::pair<Char*, std::size_t> temp(buf, N); 583 return PrintfState<std::pair<Char*, std::size_t>, Char>(temp, format); 584 } 585 586 }// namespace Loki 587 588 589 #endif // end file guardian 590 591