aboutsummaryrefslogtreecommitdiff
path: root/printer/src/mipi_syst_printf.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'printer/src/mipi_syst_printf.cpp')
-rw-r--r--printer/src/mipi_syst_printf.cpp435
1 files changed, 435 insertions, 0 deletions
diff --git a/printer/src/mipi_syst_printf.cpp b/printer/src/mipi_syst_printf.cpp
new file mode 100644
index 0000000..e0da037
--- /dev/null
+++ b/printer/src/mipi_syst_printf.cpp
@@ -0,0 +1,435 @@
+/*
+ Copyright (c) 2018, MIPI Alliance, Inc.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+ * Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/*
+ * Contributors:
+ * Norbert Schulz (Intel Corporation) - Initial API and implementation
+ */
+
+#include <sstream>
+#include "mipi_syst_printf.h"
+
+MIPI_SYST_NAMESPACE_BEGIN
+
+template<> double bytes2ValLE<double>(const void * data)
+{
+ union { uint64_t ll; double d; } u = { bytes2ValLE<uint64_t>(data) };
+ return u.d;
+}
+
+// Parse printf style format string into a sequence of text and format fragments
+//
+uint32_t
+fmtscanner::parse(const std::string& fmt, Args& args)
+{
+ uint32_t result(OK);
+
+ const char * p(fmt.c_str());
+ const char * eof(p + fmt.size());
+ FmtScanState state(START);
+ Modifier modifier(MOD_NONE);
+ size_t fmtPos = 0; // curremt position in format
+ size_t fragmentStart = 0; // where current fragment started
+ size_t lastPercentPos = 0; // where the last % fmt start was seen
+
+ args.clear();
+
+ while (p < eof) {
+ switch (state) {
+ case START:
+ fragmentStart = fmtPos;
+ modifier = MOD_NONE;
+
+ state = PLAINTEXT;
+ ; // deliberate fall through
+
+ case PLAINTEXT:
+ if (*p == '%') {
+ lastPercentPos = fmtPos;
+ state = PERCENT;
+ }
+ break;
+
+ case PERCENT:
+ if (*p == '%') {
+ lastPercentPos = 0; // '%%' is not a format, but the % char
+ state = PLAINTEXT;
+ } else {
+ // arg fmt definition is starting
+ //
+ if (lastPercentPos != fragmentStart) {
+ args.push_back(
+ ArgFragment(TEXT, 0,
+ fmt.substr(fragmentStart,
+ lastPercentPos - fragmentStart)));
+ }
+ fragmentStart = lastPercentPos;
+ state = FLAGS;
+ continue;
+ }
+ break;
+
+ case FLAGS:
+ switch (*p) {
+ case '-':
+ case '+':
+ case ' ':
+ case '#':
+ case '0':
+ break;
+ default:
+ state = WIDTH;
+ continue;
+ break;
+ }
+ break;
+
+ case WIDTH:
+ if (*p == '*') {
+ args.push_back(ArgFragment(STAR, SIZEOF_INT, ""));
+ state = PRECISION_DOT;
+ } else {
+ state = WIDTH_NUMBER;
+ continue;
+ }
+ break;
+
+ case WIDTH_NUMBER:
+ if (!isdigit(*p)) {
+ state = PRECISION_DOT;
+ continue;
+ }
+ break;
+
+ case PRECISION_DOT:
+ if (*p == '.') {
+ state = PRECISION_VAL;
+ } else {
+ state = MODIFIER;
+ continue;
+ }
+ break;
+
+ case PRECISION_VAL:
+ if (*p == '*') {
+ args.push_back(ArgFragment(STAR, SIZEOF_INT, std::string()));
+ state = MODIFIER;
+ } else {
+ state = PRECISION_NUMBER;
+ continue;
+ }
+ break;
+
+ case PRECISION_NUMBER:
+ if (!isdigit(*p)) {
+ state = MODIFIER;
+ continue;
+ }
+ break;
+
+ case MODIFIER:
+ state = SPECIFIER;
+
+ switch (*p) {
+ case 'h':
+ modifier = MOD_H;
+ state = MODIFIER_HALF;
+ break;
+ case 'l':
+ modifier = MOD_L;
+ state = MODIFIER_LONG;
+ break;
+ case 'j':
+ modifier = MOD_J;
+ break;
+ case 'z':
+ modifier = MOD_Z;
+ break;
+ case 't':
+ modifier = MOD_T;
+ break;
+ case 'L':
+ modifier = MOD_LD;
+ break;
+ default:
+ continue;
+ }
+ break;
+
+ case MODIFIER_HALF:
+ state = SPECIFIER;
+ if (*p == 'h') {
+ modifier = MOD_HH;
+ break;
+ } else {
+ continue;
+ }
+ break;
+
+
+ case MODIFIER_LONG:
+ state = SPECIFIER;
+ if (*p == 'l') {
+ modifier = MOD_LL;
+ break;
+ } else {
+ continue;
+ }
+
+ case SPECIFIER:
+ {
+ std::string fragment = fmt.substr(fragmentStart,
+ fmtPos - fragmentStart + 1);
+
+ switch (*p) {
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ switch (modifier) {
+ case MOD_L:
+ // convert long to longlong if client is 64bit, but host 32bit
+ //
+ if ((SIZEOF_LONG == 8) && (sizeof(long) != 8)) {
+ size_t pos(fragment.find('l'));
+ if (pos != std::string::npos) {
+ fragment = fragment.insert(pos, "l");
+ }
+ }
+ args.push_back(ArgFragment(INTEGER, SIZEOF_LONG, fragment));
+ break;
+ case MOD_LL:
+ case MOD_J:
+ args.push_back(ArgFragment(INTEGER, SIZEOF_LONGLONG, fragment));
+ break;
+ case MOD_Z:
+ args.push_back(ArgFragment(INTEGER, SIZEOF_SIZEOF, fragment));
+ break;
+ default:
+ args.push_back(ArgFragment(INTEGER, SIZEOF_INT, fragment));
+ break;
+ }
+ state = START;
+ break;
+ case 'f':
+ case 'F':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ case 'a':
+ case 'A':
+ args.push_back(ArgFragment(DOUBLE, SIZEOF_LONGLONG, fragment));
+ break;
+ case 'c':
+ args.push_back(ArgFragment(INTEGER, SIZEOF_INT, fragment));
+ break;
+ case 'p':
+ args.push_back(ArgFragment(POINTER, SIZEOF_PTR, fragment));
+ break;
+ case 's':
+ args.push_back(ArgFragment(STRING, SIZEOF_PTR, fragment));
+ break;
+ case 'n':
+ args.push_back(ArgFragment(PERCENT_N, SIZEOF_PTR, fragment));
+ break;
+ default: // unsupported format
+ args.push_back(ArgFragment(UNKNOWN, 0, fragment));
+ result |= UNKNOWN_FORMAT;
+ break;
+ }
+ state = START;
+ }
+ break;
+
+ default:
+ break;
+ }
+ ++p;
+ ++fmtPos;
+ }
+
+ // check if we have a tail plain text string at the end of the format
+ // and add it to arg list.
+ //
+ if (state == PLAINTEXT) {
+ args.push_back(ArgFragment(TEXT, 0,
+ fmt.substr(fragmentStart, fmtPos - fragmentStart + 1)));
+ state = START;
+ }
+
+ if (state != START) {
+ result |= INCOMPLETE_FORMAT;
+ }
+
+ return result;
+}
+
+
+fmtscanner::ArgFragment::ArgFragment(
+ ArgType type, size_t size, const std::string& text) :
+ _type(type), _size(size), _text(text)
+{}
+
+
+
+bool msgprintf::format(
+ const std::string& fmt,
+ const void * args, uint32_t size,
+ std::string& result)
+{
+ std::stringstream sstr; // where the result is build
+
+ // Parse the format string for the number of needed parameters
+ //
+ uint32_t parseResult;
+ if ((parseResult = _scanner.parse(fmt, _args)) != fmtscanner::OK) {
+ sstr << "invalid format string '" << fmt << "'";
+ result = sstr.str();
+ return false;
+ }
+
+ const char * bufPtr((const char *)args); //current byte from arg buffer
+ const void * eob((const char *)args + size); // end of buffer address
+ std::vector<int> starArgs; // '*' args like in "%*.*f"
+ bool success(true);
+
+ // Loop over the argument fragments to format each using local printf
+ // and then add it to the result string.
+ //
+ for (fmtscanner::Args::const_iterator it(_args.begin());
+ it != _args.end();
+ ++it)
+ {
+ size_t need(it->size()); // number of bytes needed for format
+ fmtscanner::ArgType type(it->type());
+
+ switch (type) {
+ case fmtscanner::INTEGER:
+ case fmtscanner::POINTER:
+ if (bufPtr + need <= eob) {
+ if (need == sizeof(uint64_t)) {
+ uint64_t val(bytes2ValLE<uint64_t>(bufPtr));
+ success = hostPrintf(sstr, it->text(), val, starArgs);
+ } else {
+ uint32_t val(bytes2ValLE<uint32_t>(bufPtr));
+ success = hostPrintf(sstr, it->text(), val, starArgs);
+ }
+ }
+ break;
+
+ case fmtscanner::DOUBLE:
+ if (bufPtr + need <= eob) {
+ double val(bytes2ValLE<double>(bufPtr));
+ success = hostPrintf(sstr, it->text(), val, starArgs);
+ }
+ break;
+
+ case fmtscanner::TEXT:
+ // copy text, but "%%" means "%" in printf format
+ //
+ for (const char *c = it->text().c_str(); *c; ++c) {
+ if ('%' == *c && '%' == *(c+1)) continue;
+ sstr << *c;
+ }
+
+ break;
+
+ case fmtscanner::STRING:
+ // String embedded in message buffer
+ // check if it is valid (zero terminated inside message) and
+ // then format it into result
+ //
+ {
+ const char * str(bufPtr);
+
+ // Hunt the null byte
+ //
+ for (need = 1; str <= eob && *str; ++str, ++need)
+ ;
+
+ if (str > eob) {
+ result = " - corrupt printf payload, missing argument string termination";
+ return false;
+ }
+
+ success = hostPrintf(sstr, it->text(), bufPtr, starArgs);
+ }
+ break;
+ case fmtscanner::PERCENT_N:
+ // %n is a nop . There is no usefull place to return the
+ // output to.
+ //
+ break;
+
+ case fmtscanner::STAR:
+ // A star argument was seen in fmt, put on the *-stack (can be up to 2)
+ //
+ if (bufPtr + need <= eob) {
+ starArgs.push_back(bytes2ValLE<uint32_t>(bufPtr));
+ }
+ break;
+
+ default:
+ sstr << "- printf internal error, unkown argtype in format '"
+ << fmt << "'";
+ success = false;
+ }
+
+ // Check for overflow in input buffer
+ //
+ if ((bufPtr + need) > eob) {
+ sstr << "- insufficient argument bytes for format '"
+ << fmt << "'";
+ success = false;
+ }
+
+ if (!success) {
+ result = sstr.str();
+ return false;
+ }
+
+ if (type != fmtscanner::STAR) {
+ starArgs.clear();
+ }
+ bufPtr += need;
+ }
+
+ result = sstr.str();
+
+ return true;
+}
+
+MIPI_SYST_NAMESPACE_END \ No newline at end of file