1/*
2 * Copyright (c) 2002-2006 The Regents of The University of Michigan
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met: redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer;
9 * redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution;
12 * neither the name of the copyright holders nor the names of its
13 * contributors may be used to endorse or promote products derived from
14 * this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Authors: Nathan Binkert
29 */
30
31#include "base/cprintf.hh"
32
33#include <cassert>
34#include <iomanip>
35#include <iostream>
36#include <sstream>
37
38#include "base/compiler.hh"
39
40using namespace std;
41
42namespace cp {
43
44Print::Print(std::ostream &stream, const std::string &format)
45    : stream(stream), format(format.c_str()), ptr(format.c_str()), cont(false)
46{
47    saved_flags = stream.flags();
48    saved_fill = stream.fill();
49    saved_precision = stream.precision();
50    saved_width = stream.width();
51}
52
53Print::Print(std::ostream &stream, const char *format)
54    : stream(stream), format(format), ptr(format), cont(false)
55{
56    saved_flags = stream.flags();
57    saved_fill = stream.fill();
58    saved_precision = stream.precision();
59    saved_width = stream.width();
60}
61
62Print::~Print()
63{
64}
65
66void
67Print::process()
68{
69    fmt.clear();
70
71    size_t len;
72
73    while (*ptr) {
74        switch (*ptr) {
75          case '%':
76            if (ptr[1] != '%') {
77                process_flag();
78                return;
79            }
80            stream.put('%');
81            ptr += 2;
82            break;
83
84          case '\n':
85            stream << endl;
86            ++ptr;
87            break;
88          case '\r':
89            ++ptr;
90            if (*ptr != '\n')
91                stream << endl;
92            break;
93
94          default:
95            len = strcspn(ptr, "%\n\r\0");
96            stream.write(ptr, len);
97            ptr += len;
98            break;
99        }
100    }
101}
102
103void
104Print::process_flag()
105{
106    bool done = false;
107    bool end_number = false;
108    bool have_precision = false;
109    int number = 0;
110
111    stream.fill(' ');
112    stream.flags((ios::fmtflags)0);
113
114    while (!done) {
115        ++ptr;
116        if (*ptr >= '0' && *ptr <= '9') {
117            if (end_number)
118                continue;
119        } else if (number > 0)
120            end_number = true;
121
122        switch (*ptr) {
123          case 's':
124            fmt.format = Format::string;
125            done = true;
126            break;
127
128          case 'c':
129            fmt.format = Format::character;
130            done = true;
131            break;
132
133          case 'l':
134            continue;
135
136          case 'p':
137            fmt.format = Format::integer;
138            fmt.base = Format::hex;
139            fmt.alternate_form = true;
140            done = true;
141            break;
142
143          case 'X':
144            fmt.uppercase = true;
145            M5_FALLTHROUGH;
146          case 'x':
147            fmt.base = Format::hex;
148            fmt.format = Format::integer;
149            done = true;
150            break;
151
152          case 'o':
153            fmt.base = Format::oct;
154            fmt.format = Format::integer;
155            done = true;
156            break;
157
158          case 'd':
159          case 'i':
160          case 'u':
161            fmt.format = Format::integer;
162            done = true;
163            break;
164
165          case 'G':
166            fmt.uppercase = true;
167            M5_FALLTHROUGH;
168          case 'g':
169            fmt.format = Format::floating;
170            fmt.float_format = Format::best;
171            done = true;
172            break;
173
174          case 'E':
175            fmt.uppercase = true;
176            M5_FALLTHROUGH;
177          case 'e':
178            fmt.format = Format::floating;
179            fmt.float_format = Format::scientific;
180            done = true;
181            break;
182
183          case 'f':
184            fmt.format = Format::floating;
185            fmt.float_format = Format::fixed;
186            done = true;
187            break;
188
189          case 'n':
190            stream << "we don't do %n!!!\n";
191            done = true;
192            break;
193
194          case '#':
195            fmt.alternate_form = true;
196            break;
197
198          case '-':
199            fmt.flush_left = true;
200            break;
201
202          case '+':
203            fmt.print_sign = true;
204            break;
205
206          case ' ':
207            fmt.blank_space = true;
208            break;
209
210          case '.':
211            fmt.width = number;
212            fmt.precision = 0;
213            have_precision = true;
214            number = 0;
215            end_number = false;
216            break;
217
218          case '0':
219            if (number == 0) {
220                fmt.fill_zero = true;
221                break;
222            }
223            M5_FALLTHROUGH;
224          case '1':
225          case '2':
226          case '3':
227          case '4':
228          case '5':
229          case '6':
230          case '7':
231          case '8':
232          case '9':
233            number = number * 10 + (*ptr - '0');
234            break;
235
236          case '*':
237            if (have_precision)
238                fmt.get_precision = true;
239            else
240                fmt.get_width = true;
241            break;
242
243          case '%':
244            assert(false && "we shouldn't get here");
245            break;
246
247          default:
248            done = true;
249            break;
250        }
251
252        if (end_number) {
253            if (have_precision)
254                fmt.precision = number;
255            else
256                fmt.width = number;
257
258            end_number = false;
259            number = 0;
260        }
261
262        if (done) {
263            if ((fmt.format == Format::integer) && have_precision) {
264                // specified a . but not a float, set width
265                fmt.width = fmt.precision;
266                // precision requries digits for width, must fill with 0
267                fmt.fill_zero = true;
268            } else if ((fmt.format == Format::floating) && !have_precision &&
269                        fmt.fill_zero) {
270                // ambiguous case, matching printf
271                fmt.precision = fmt.width;
272            }
273        }
274    } // end while
275
276    ++ptr;
277}
278
279void
280Print::end_args()
281{
282    size_t len;
283
284    while (*ptr) {
285        switch (*ptr) {
286          case '%':
287            if (ptr[1] != '%')
288                stream << "<extra arg>";
289
290            stream.put('%');
291            ptr += 2;
292            break;
293
294          case '\n':
295            stream << endl;
296            ++ptr;
297            break;
298          case '\r':
299            ++ptr;
300            if (*ptr != '\n')
301                stream << endl;
302            break;
303
304          default:
305            len = strcspn(ptr, "%\n\r\0");
306            stream.write(ptr, len);
307            ptr += len;
308            break;
309        }
310    }
311
312    stream.flags(saved_flags);
313    stream.fill(saved_fill);
314    stream.precision(saved_precision);
315    stream.width(saved_width);
316}
317
318} // namespace cp
319