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