1 /*
2 Copyright (c) 2018, MIPI Alliance, Inc.
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
7 are met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in
14 the documentation and/or other materials provided with the
15 distribution.
16
17 * Neither the name of the copyright holder nor the names of its
18 contributors may be used to endorse or promote products derived
19 from this software without specific prior written permission.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * Contributors:
36 * Norbert Schulz (Intel Corporation) - Initial API and implementation
37 */
38
39 #include <sstream>
40 #include "mipi_syst_printf.h"
41
42 MIPI_SYST_NAMESPACE_BEGIN
43
bytes2ValLE(const void * data)44 template<> double bytes2ValLE<double>(const void * data)
45 {
46 union { uint64_t ll; double d; } u = { bytes2ValLE<uint64_t>(data) };
47 return u.d;
48 }
49
50 // Parse printf style format string into a sequence of text and format fragments
51 //
52 uint32_t
parse(const std::string & fmt,Args & args)53 fmtscanner::parse(const std::string& fmt, Args& args)
54 {
55 uint32_t result(OK);
56
57 const char * p(fmt.c_str());
58 const char * eof(p + fmt.size());
59 FmtScanState state(START);
60 Modifier modifier(MOD_NONE);
61 size_t fmtPos = 0; // curremt position in format
62 size_t fragmentStart = 0; // where current fragment started
63 size_t lastPercentPos = 0; // where the last % fmt start was seen
64
65 args.clear();
66
67 while (p < eof) {
68 switch (state) {
69 case START:
70 fragmentStart = fmtPos;
71 modifier = MOD_NONE;
72
73 state = PLAINTEXT;
74 ; // deliberate fall through
75
76 case PLAINTEXT:
77 if (*p == '%') {
78 lastPercentPos = fmtPos;
79 state = PERCENT;
80 }
81 break;
82
83 case PERCENT:
84 if (*p == '%') {
85 lastPercentPos = 0; // '%%' is not a format, but the % char
86 state = PLAINTEXT;
87 } else {
88 // arg fmt definition is starting
89 //
90 if (lastPercentPos != fragmentStart) {
91 args.push_back(
92 ArgFragment(TEXT, 0,
93 fmt.substr(fragmentStart,
94 lastPercentPos - fragmentStart)));
95 }
96 fragmentStart = lastPercentPos;
97 state = FLAGS;
98 continue;
99 }
100 break;
101
102 case FLAGS:
103 switch (*p) {
104 case '-':
105 case '+':
106 case ' ':
107 case '#':
108 case '0':
109 break;
110 default:
111 state = WIDTH;
112 continue;
113 break;
114 }
115 break;
116
117 case WIDTH:
118 if (*p == '*') {
119 args.push_back(ArgFragment(STAR, SIZEOF_INT, ""));
120 state = PRECISION_DOT;
121 } else {
122 state = WIDTH_NUMBER;
123 continue;
124 }
125 break;
126
127 case WIDTH_NUMBER:
128 if (!isdigit(*p)) {
129 state = PRECISION_DOT;
130 continue;
131 }
132 break;
133
134 case PRECISION_DOT:
135 if (*p == '.') {
136 state = PRECISION_VAL;
137 } else {
138 state = MODIFIER;
139 continue;
140 }
141 break;
142
143 case PRECISION_VAL:
144 if (*p == '*') {
145 args.push_back(ArgFragment(STAR, SIZEOF_INT, std::string()));
146 state = MODIFIER;
147 } else {
148 state = PRECISION_NUMBER;
149 continue;
150 }
151 break;
152
153 case PRECISION_NUMBER:
154 if (!isdigit(*p)) {
155 state = MODIFIER;
156 continue;
157 }
158 break;
159
160 case MODIFIER:
161 state = SPECIFIER;
162
163 switch (*p) {
164 case 'h':
165 modifier = MOD_H;
166 state = MODIFIER_HALF;
167 break;
168 case 'l':
169 modifier = MOD_L;
170 state = MODIFIER_LONG;
171 break;
172 case 'j':
173 modifier = MOD_J;
174 break;
175 case 'z':
176 modifier = MOD_Z;
177 break;
178 case 't':
179 modifier = MOD_T;
180 break;
181 case 'L':
182 modifier = MOD_LD;
183 break;
184 default:
185 continue;
186 }
187 break;
188
189 case MODIFIER_HALF:
190 state = SPECIFIER;
191 if (*p == 'h') {
192 modifier = MOD_HH;
193 break;
194 } else {
195 continue;
196 }
197 break;
198
199
200 case MODIFIER_LONG:
201 state = SPECIFIER;
202 if (*p == 'l') {
203 modifier = MOD_LL;
204 break;
205 } else {
206 continue;
207 }
208
209 case SPECIFIER:
210 {
211 std::string fragment = fmt.substr(fragmentStart,
212 fmtPos - fragmentStart + 1);
213
214 switch (*p) {
215 case 'd':
216 case 'i':
217 case 'u':
218 case 'o':
219 case 'x':
220 case 'X':
221 switch (modifier) {
222 case MOD_L:
223 // convert long to longlong if client is 64bit, but host 32bit
224 //
225 if ((SIZEOF_LONG == 8) && (sizeof(long) != 8)) {
226 size_t pos(fragment.find('l'));
227 if (pos != std::string::npos) {
228 fragment = fragment.insert(pos, "l");
229 }
230 }
231 args.push_back(ArgFragment(INTEGER, SIZEOF_LONG, fragment));
232 break;
233 case MOD_LL:
234 case MOD_J:
235 args.push_back(ArgFragment(INTEGER, SIZEOF_LONGLONG, fragment));
236 break;
237 case MOD_Z:
238 args.push_back(ArgFragment(INTEGER, SIZEOF_SIZEOF, fragment));
239 break;
240 default:
241 args.push_back(ArgFragment(INTEGER, SIZEOF_INT, fragment));
242 break;
243 }
244 state = START;
245 break;
246 case 'f':
247 case 'F':
248 case 'e':
249 case 'E':
250 case 'g':
251 case 'G':
252 case 'a':
253 case 'A':
254 args.push_back(ArgFragment(DOUBLE, SIZEOF_LONGLONG, fragment));
255 break;
256 case 'c':
257 args.push_back(ArgFragment(INTEGER, SIZEOF_INT, fragment));
258 break;
259 case 'p':
260 args.push_back(ArgFragment(POINTER, SIZEOF_PTR, fragment));
261 break;
262 case 's':
263 args.push_back(ArgFragment(STRING, SIZEOF_PTR, fragment));
264 break;
265 case 'n':
266 args.push_back(ArgFragment(PERCENT_N, SIZEOF_PTR, fragment));
267 break;
268 default: // unsupported format
269 args.push_back(ArgFragment(UNKNOWN, 0, fragment));
270 result |= UNKNOWN_FORMAT;
271 break;
272 }
273 state = START;
274 }
275 break;
276
277 default:
278 break;
279 }
280 ++p;
281 ++fmtPos;
282 }
283
284 // check if we have a tail plain text string at the end of the format
285 // and add it to arg list.
286 //
287 if (state == PLAINTEXT) {
288 args.push_back(ArgFragment(TEXT, 0,
289 fmt.substr(fragmentStart, fmtPos - fragmentStart + 1)));
290 state = START;
291 }
292
293 if (state != START) {
294 result |= INCOMPLETE_FORMAT;
295 }
296
297 return result;
298 }
299
300
ArgFragment(ArgType type,size_t size,const std::string & text)301 fmtscanner::ArgFragment::ArgFragment(
302 ArgType type, size_t size, const std::string& text) :
303 _type(type), _size(size), _text(text)
304 {}
305
306
307
format(const std::string & fmt,const void * args,uint32_t size,std::string & result)308 bool msgprintf::format(
309 const std::string& fmt,
310 const void * args, uint32_t size,
311 std::string& result)
312 {
313 std::stringstream sstr; // where the result is build
314
315 // Parse the format string for the number of needed parameters
316 //
317 uint32_t parseResult;
318 if ((parseResult = _scanner.parse(fmt, _args)) != fmtscanner::OK) {
319 sstr << "invalid format string '" << fmt << "'";
320 result = sstr.str();
321 return false;
322 }
323
324 const char * bufPtr((const char *)args); //current byte from arg buffer
325 const void * eob((const char *)args + size); // end of buffer address
326 std::vector<int> starArgs; // '*' args like in "%*.*f"
327 bool success(true);
328
329 // Loop over the argument fragments to format each using local printf
330 // and then add it to the result string.
331 //
332 for (fmtscanner::Args::const_iterator it(_args.begin());
333 it != _args.end();
334 ++it)
335 {
336 size_t need(it->size()); // number of bytes needed for format
337 fmtscanner::ArgType type(it->type());
338
339 switch (type) {
340 case fmtscanner::INTEGER:
341 case fmtscanner::POINTER:
342 if (bufPtr + need <= eob) {
343 if (need == sizeof(uint64_t)) {
344 uint64_t val(bytes2ValLE<uint64_t>(bufPtr));
345 success = hostPrintf(sstr, it->text(), val, starArgs);
346 } else {
347 uint32_t val(bytes2ValLE<uint32_t>(bufPtr));
348 success = hostPrintf(sstr, it->text(), val, starArgs);
349 }
350 }
351 break;
352
353 case fmtscanner::DOUBLE:
354 if (bufPtr + need <= eob) {
355 double val(bytes2ValLE<double>(bufPtr));
356 success = hostPrintf(sstr, it->text(), val, starArgs);
357 }
358 break;
359
360 case fmtscanner::TEXT:
361 // copy text, but "%%" means "%" in printf format
362 //
363 for (const char *c = it->text().c_str(); *c; ++c) {
364 if ('%' == *c && '%' == *(c+1)) continue;
365 sstr << *c;
366 }
367
368 break;
369
370 case fmtscanner::STRING:
371 // String embedded in message buffer
372 // check if it is valid (zero terminated inside message) and
373 // then format it into result
374 //
375 {
376 const char * str(bufPtr);
377
378 // Hunt the null byte
379 //
380 for (need = 1; str <= eob && *str; ++str, ++need)
381 ;
382
383 if (str > eob) {
384 result = " - corrupt printf payload, missing argument string termination";
385 return false;
386 }
387
388 success = hostPrintf(sstr, it->text(), bufPtr, starArgs);
389 }
390 break;
391 case fmtscanner::PERCENT_N:
392 // %n is a nop . There is no usefull place to return the
393 // output to.
394 //
395 break;
396
397 case fmtscanner::STAR:
398 // A star argument was seen in fmt, put on the *-stack (can be up to 2)
399 //
400 if (bufPtr + need <= eob) {
401 starArgs.push_back(bytes2ValLE<uint32_t>(bufPtr));
402 }
403 break;
404
405 default:
406 sstr << "- printf internal error, unkown argtype in format '"
407 << fmt << "'";
408 success = false;
409 }
410
411 // Check for overflow in input buffer
412 //
413 if ((bufPtr + need) > eob) {
414 sstr << "- insufficient argument bytes for format '"
415 << fmt << "'";
416 success = false;
417 }
418
419 if (!success) {
420 result = sstr.str();
421 return false;
422 }
423
424 if (type != fmtscanner::STAR) {
425 starArgs.clear();
426 }
427 bufPtr += need;
428 }
429
430 result = sstr.str();
431
432 return true;
433 }
434
435 MIPI_SYST_NAMESPACE_END