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