20130313
[gdash.git] / src / misc / printf.cpp
blobcc7244da4f8dbd01dbe18393c225397be92465e5
1 /*
2 * Copyright (c) 2007-2013, Czirkos Zoltan http://code.google.com/p/gdash/
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 #include "config.h"
19 #include <iomanip>
20 #include <string>
21 #include <sstream>
22 #include <stdexcept>
23 #include <cassert>
25 #include "cave/cavetypes.hpp"
27 #include "misc/printf.hpp"
29 /// All conversion specifiers which are recognized by the class in the format string, eg. printf %s, %d, %c etc.
30 const char *Printf::conv_specifiers="sdiucfgx";
31 /// Conversion modifiers supported.
32 std::string Printf::flag_characters="0-+lhm";
35 /**
36 * @brief This function html-markups a string.
37 * It will change <, >, &, and \n characters into &lt; &gt; &amp; and <br>.
39 std::string Printf::html_markup_text(const std::string& of_what) {
40 std::string result;
42 for (unsigned i=0; i<of_what.size(); ++i) {
43 switch (of_what[i]) {
44 case '<':
45 result+="&lt;";
46 break;
47 case '>':
48 result+="&lt;";
49 break;
50 case '&':
51 result+="&amp;";
52 break;
53 case '\n':
54 result+="\n<br>\n";
55 break;
56 default:
57 result+=of_what[i];
58 break;
62 return result;
66 /// Printf object constructor.
67 /// @param percent Character which specifies a conversion. Default is %, as in misc/printf.
68 /// @param format The format string.
69 Printf::Printf(const std::string& format_, char percent)
71 format(format_),
72 inserted_chars(0) {
73 size_t pos, nextpos=0;
74 // search for the next conversion specifier.
75 // the position is stored in pos.
76 // if a %% is found, it is replaced with %, and the search is continued.
77 while ((pos=format.find(percent, nextpos))!=std::string::npos) {
78 if (pos+1==format.length())
79 throw std::runtime_error("unterminated conversion specifier at the end of the string");
80 if (format[pos+1]==percent) {
81 /* this is just a percent sign */
82 format.erase(pos, 1);
83 nextpos=pos+1;
84 } else {
85 /* this is a conversion specifier. */
86 size_t last=format.find_first_of(conv_specifiers, pos+1);
87 if (last==std::string::npos)
88 throw std::runtime_error("unterminated conversion specifier");
90 // ok we found something like %-5s. get the conversion type (s), and get
91 // the manipulator (-5).
92 Conversion c;
93 c.pos = pos;
94 c.conv = format[last];
95 c.manip = format.substr(pos+1, last-pos-1);
96 c.html_markup = format.find('m') != std::string::npos;
97 conversions.push_back(c);
98 // now delete the conversion specifier from the string.
99 format.erase(pos, last-pos+1);
100 nextpos=pos;
105 /// This function finds the next conversion specifier in the format string,
106 /// and sets the ostringstream accordingly.
107 /// This is put in a separate function, so the templated function (operator%) is
108 /// not too long - to prevent code bloat.
109 /// Also it removes the conversion specifier from the format string.
110 /// @param os The ostringstream to setup according to the next found conversion specifier.
111 /// @param pos The position, which is the char position of the original conversion specifier.
112 void Printf::configure_ostream(std::ostringstream& os, Conversion &conversion) const {
113 // process format specifier.
114 // the type of the variable to be written is mainly handled by
115 // the c++ type system. here we only take care of the small
116 // differences.
117 if (conversion.conv=='x')
118 os<<std::hex; // %x used to print in hexadecimal
120 // if we have a manipulator, is it at the beginning of the string
121 while (flag_characters.find_first_of(conversion.manip[0])!=std::string::npos) {
122 switch(conversion.manip[0]) {
123 case '-':
124 os << std::left;
125 break;
126 case '0':
127 os.fill('0');
128 break;
129 case '+':
130 os << std::showpos;
131 break;
132 case 'l':
133 case 'h':
134 // do nothing; for compatibility with printf;
135 break;
136 case 'm':
137 // html markup; already noted it in the conversion
138 break;
139 default:
140 assert(!"don't know how to process flag character");
141 break;
143 conversion.manip.erase(0, 1); // erase processed flag from the string
145 // if the manipulator is not empty, it must be a width [. precision].
146 if (!conversion.manip.empty()) {
147 std::istringstream is(conversion.manip);
148 unsigned width;
149 is>>width;
150 if (is)
151 os.width(width);
152 is.clear(); // clear error state, as we might not have had a width specifier
153 char c;
154 if (is>>c) {
155 unsigned precision;
156 is>>precision;
157 if (!is)
158 throw std::runtime_error("invalid precision");
159 os << std::fixed; // so it is the same as printf %6.4 -> [3.1400]
160 os.precision(precision);
165 /// This function puts the contents of the ostringstream to the
166 /// string at the given position.
167 /// @param os The ostringstream, which should already contain the data formatted.
168 /// @param pos The position to insert the contents of the string at.
169 void Printf::insert_converted(std::string const &str, Conversion &conversion) const {
170 // add inserted_chars to the originally calculated position - as
171 // before this conversion, the already finished conversions added that much characters before the current position
172 format.insert(conversion.pos + inserted_chars, str);
173 // and remember the next successive position
174 inserted_chars += str.length();