\brief Process a printf message using the system printf function. * \param begin Start of the uint64_t array containing the message. * \param end One past the last element in the array. * \return An integer that satisfies the POSIX return value for printf. * * The message has the following format: * - uint64_t version, required to be zero. * - Format string padded to an 8 byte boundary.
| 166 | * - %n specifier is ignored and the corresponding argument is skipped. |
| 167 | */ |
| 168 | static int format(FILE* stream, const uint64_t* begin, const uint64_t* end) { |
| 169 | const char convSpecifiers[] = "diouxXfFeEgGaAcspn"; |
| 170 | auto ptr = begin; |
| 171 | |
| 172 | const std::string fmt(reinterpret_cast<const char*>(ptr)); |
| 173 | ptr += (fmt.length() + 7 + 1) / 8; // the extra '1' is for the null |
| 174 | |
| 175 | int outCount = 0; |
| 176 | size_t point = 0; |
| 177 | while (true) { |
| 178 | // Each segment of the format string delineated by [mark, |
| 179 | // point) is handled seprately. |
| 180 | auto mark = point; |
| 181 | point = fmt.find('%', point); |
| 182 | |
| 183 | // Two different cases where a literal segment is printed out. |
| 184 | // 1. When the point reaches the end of the format string. |
| 185 | // 2. When the point is at the start of a format specifier. |
| 186 | if (point == std::string::npos) { |
| 187 | checkPrintf(stream, &outCount, "%s", &fmt[mark]); |
| 188 | return outCount; |
| 189 | } |
| 190 | checkPrintf(stream, &outCount, "%.*s", (int)(point - mark), &fmt[mark]); |
| 191 | if (outCount < 0) { |
| 192 | return outCount; |
| 193 | } |
| 194 | |
| 195 | mark = point; |
| 196 | ++point; |
| 197 | |
| 198 | // Handle the simplest specifier, '%%'. |
| 199 | if (fmt[point] == '%') { |
| 200 | checkPrintf(stream, &outCount, "%%"); |
| 201 | if (outCount < 0) { |
| 202 | return outCount; |
| 203 | } |
| 204 | ++point; |
| 205 | continue; |
| 206 | } |
| 207 | |
| 208 | // Before processing the specifier, check if we have run out |
| 209 | // of arguments. |
| 210 | if (ptr == end) { |
| 211 | return outCount; |
| 212 | } |
| 213 | |
| 214 | // Undefined behaviour if we don't see a conversion specifier. |
| 215 | point = fmt.find_first_of(convSpecifiers, point); |
| 216 | if (point == std::string::npos) { |
| 217 | return outCount; |
| 218 | } |
| 219 | ++point; |
| 220 | |
| 221 | // [mark,point) now contains a complete specifier. |
| 222 | const std::string spec(fmt, mark, point - mark); |
| 223 | ptr = processSpec(stream, &outCount, spec, ptr, end); |
| 224 | if (outCount < 0) { |
| 225 | return outCount; |
no test coverage detected