chprintf.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. /*
  2. ChibiOS - Copyright (C) 2006..2018 Giovanni Di Sirio
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. /*
  14. Concepts and parts of this file have been contributed by Fabio Utzig,
  15. chvprintf() added by Brent Roman.
  16. */
  17. /**
  18. * @file chprintf.c
  19. * @brief Mini printf-like functionality.
  20. *
  21. * @addtogroup HAL_CHPRINTF
  22. * @details Mini printf-like functionality.
  23. * @{
  24. */
  25. #include "hal.h"
  26. #include "chprintf.h"
  27. #include "memstreams.h"
  28. #define MAX_FILLER 11
  29. #define FLOAT_PRECISION 9
  30. static char *long_to_string_with_divisor(char *p,
  31. long num,
  32. unsigned radix,
  33. long divisor) {
  34. int i;
  35. char *q;
  36. long l, ll;
  37. l = num;
  38. if (divisor == 0) {
  39. ll = num;
  40. } else {
  41. ll = divisor;
  42. }
  43. q = p + MAX_FILLER;
  44. do {
  45. i = (int)(l % radix);
  46. i += '0';
  47. if (i > '9')
  48. i += 'A' - '0' - 10;
  49. *--q = i;
  50. l /= radix;
  51. } while ((ll /= radix) != 0);
  52. i = (int)(p + MAX_FILLER - q);
  53. do
  54. *p++ = *q++;
  55. while (--i);
  56. return p;
  57. }
  58. static char *ch_ltoa(char *p, long num, unsigned radix) {
  59. return long_to_string_with_divisor(p, num, radix, 0);
  60. }
  61. #if CHPRINTF_USE_FLOAT
  62. static const long pow10[FLOAT_PRECISION] = {
  63. 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
  64. };
  65. static char *ftoa(char *p, double num, unsigned long precision) {
  66. long l;
  67. if ((precision == 0) || (precision > FLOAT_PRECISION))
  68. precision = FLOAT_PRECISION;
  69. precision = pow10[precision - 1];
  70. l = (long)num;
  71. p = long_to_string_with_divisor(p, l, 10, 0);
  72. *p++ = '.';
  73. l = (long)((num - l) * precision);
  74. return long_to_string_with_divisor(p, l, 10, precision / 10);
  75. }
  76. #endif
  77. /**
  78. * @brief System formatted output function.
  79. * @details This function implements a minimal @p vprintf()-like functionality
  80. * with output on a @p BaseSequentialStream.
  81. * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
  82. * The following parameter types (p) are supported:
  83. * - <b>x</b> hexadecimal integer.
  84. * - <b>X</b> hexadecimal long.
  85. * - <b>o</b> octal integer.
  86. * - <b>O</b> octal long.
  87. * - <b>d</b> decimal signed integer.
  88. * - <b>D</b> decimal signed long.
  89. * - <b>u</b> decimal unsigned integer.
  90. * - <b>U</b> decimal unsigned long.
  91. * - <b>c</b> character.
  92. * - <b>s</b> string.
  93. * .
  94. *
  95. * @param[in] chp pointer to a @p BaseSequentialStream implementing object
  96. * @param[in] fmt formatting string
  97. * @param[in] ap list of parameters
  98. * @return The number of bytes that would have been
  99. * written to @p chp if no stream error occurs
  100. *
  101. * @api
  102. */
  103. int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) {
  104. char *p, *s, c, filler;
  105. int i, precision, width;
  106. int n = 0;
  107. bool is_long, left_align;
  108. long l;
  109. #if CHPRINTF_USE_FLOAT
  110. float f;
  111. char tmpbuf[2*MAX_FILLER + 1];
  112. #else
  113. char tmpbuf[MAX_FILLER + 1];
  114. #endif
  115. while (true) {
  116. c = *fmt++;
  117. if (c == 0)
  118. return n;
  119. if (c != '%') {
  120. streamPut(chp, (uint8_t)c);
  121. n++;
  122. continue;
  123. }
  124. p = tmpbuf;
  125. s = tmpbuf;
  126. left_align = FALSE;
  127. if (*fmt == '-') {
  128. fmt++;
  129. left_align = TRUE;
  130. }
  131. filler = ' ';
  132. if (*fmt == '0') {
  133. fmt++;
  134. filler = '0';
  135. }
  136. width = 0;
  137. while (TRUE) {
  138. c = *fmt++;
  139. if (c >= '0' && c <= '9')
  140. c -= '0';
  141. else if (c == '*')
  142. c = va_arg(ap, int);
  143. else
  144. break;
  145. width = width * 10 + c;
  146. }
  147. precision = 0;
  148. if (c == '.') {
  149. while (TRUE) {
  150. c = *fmt++;
  151. if (c >= '0' && c <= '9')
  152. c -= '0';
  153. else if (c == '*')
  154. c = va_arg(ap, int);
  155. else
  156. break;
  157. precision *= 10;
  158. precision += c;
  159. }
  160. }
  161. /* Long modifier.*/
  162. if (c == 'l' || c == 'L') {
  163. is_long = TRUE;
  164. if (*fmt)
  165. c = *fmt++;
  166. }
  167. else
  168. is_long = (c >= 'A') && (c <= 'Z');
  169. /* Command decoding.*/
  170. switch (c) {
  171. case 'c':
  172. filler = ' ';
  173. *p++ = va_arg(ap, int);
  174. break;
  175. case 's':
  176. filler = ' ';
  177. if ((s = va_arg(ap, char *)) == 0)
  178. s = "(null)";
  179. if (precision == 0)
  180. precision = 32767;
  181. for (p = s; *p && (--precision >= 0); p++)
  182. ;
  183. break;
  184. case 'D':
  185. case 'd':
  186. case 'I':
  187. case 'i':
  188. if (is_long)
  189. l = va_arg(ap, long);
  190. else
  191. l = va_arg(ap, int);
  192. if (l < 0) {
  193. *p++ = '-';
  194. l = -l;
  195. }
  196. p = ch_ltoa(p, l, 10);
  197. break;
  198. #if CHPRINTF_USE_FLOAT
  199. case 'f':
  200. f = (float) va_arg(ap, double);
  201. if (f < 0) {
  202. *p++ = '-';
  203. f = -f;
  204. }
  205. p = ftoa(p, f, precision);
  206. break;
  207. #endif
  208. case 'X':
  209. case 'x':
  210. c = 16;
  211. goto unsigned_common;
  212. case 'U':
  213. case 'u':
  214. c = 10;
  215. goto unsigned_common;
  216. case 'O':
  217. case 'o':
  218. c = 8;
  219. unsigned_common:
  220. if (is_long)
  221. l = va_arg(ap, unsigned long);
  222. else
  223. l = va_arg(ap, unsigned int);
  224. p = ch_ltoa(p, l, c);
  225. break;
  226. default:
  227. *p++ = c;
  228. break;
  229. }
  230. i = (int)(p - s);
  231. if ((width -= i) < 0)
  232. width = 0;
  233. if (left_align == FALSE)
  234. width = -width;
  235. if (width < 0) {
  236. if (*s == '-' && filler == '0') {
  237. streamPut(chp, (uint8_t)*s++);
  238. n++;
  239. i--;
  240. }
  241. do {
  242. streamPut(chp, (uint8_t)filler);
  243. n++;
  244. } while (++width != 0);
  245. }
  246. while (--i >= 0) {
  247. streamPut(chp, (uint8_t)*s++);
  248. n++;
  249. }
  250. while (width) {
  251. streamPut(chp, (uint8_t)filler);
  252. n++;
  253. width--;
  254. }
  255. }
  256. }
  257. /**
  258. * @brief System formatted output function.
  259. * @details This function implements a minimal @p printf() like functionality
  260. * with output on a @p BaseSequentialStream.
  261. * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
  262. * The following parameter types (p) are supported:
  263. * - <b>x</b> hexadecimal integer.
  264. * - <b>X</b> hexadecimal long.
  265. * - <b>o</b> octal integer.
  266. * - <b>O</b> octal long.
  267. * - <b>d</b> decimal signed integer.
  268. * - <b>D</b> decimal signed long.
  269. * - <b>u</b> decimal unsigned integer.
  270. * - <b>U</b> decimal unsigned long.
  271. * - <b>c</b> character.
  272. * - <b>s</b> string.
  273. * .
  274. *
  275. * @param[in] chp pointer to a @p BaseSequentialStream implementing object
  276. * @param[in] fmt formatting string
  277. * @return The number of bytes that would have been
  278. * written to @p chp if no stream error occurs
  279. *
  280. * @api
  281. */
  282. int chprintf(BaseSequentialStream *chp, const char *fmt, ...) {
  283. va_list ap;
  284. int formatted_bytes;
  285. va_start(ap, fmt);
  286. formatted_bytes = chvprintf(chp, fmt, ap);
  287. va_end(ap);
  288. return formatted_bytes;
  289. }
  290. /**
  291. * @brief System formatted output function.
  292. * @details This function implements a minimal @p snprintf()-like functionality.
  293. * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
  294. * The following parameter types (p) are supported:
  295. * - <b>x</b> hexadecimal integer.
  296. * - <b>X</b> hexadecimal long.
  297. * - <b>o</b> octal integer.
  298. * - <b>O</b> octal long.
  299. * - <b>d</b> decimal signed integer.
  300. * - <b>D</b> decimal signed long.
  301. * - <b>u</b> decimal unsigned integer.
  302. * - <b>U</b> decimal unsigned long.
  303. * - <b>c</b> character.
  304. * - <b>s</b> string.
  305. * .
  306. * @post @p str is NUL-terminated, unless @p size is 0.
  307. *
  308. * @param[in] str pointer to a buffer
  309. * @param[in] size maximum size of the buffer
  310. * @param[in] fmt formatting string
  311. * @return The number of characters (excluding the
  312. * terminating NUL byte) that would have been
  313. * stored in @p str if there was room.
  314. *
  315. * @api
  316. */
  317. int chsnprintf(char *str, size_t size, const char *fmt, ...) {
  318. va_list ap;
  319. int retval;
  320. /* Performing the print operation.*/
  321. va_start(ap, fmt);
  322. retval = chvsnprintf(str, size, fmt, ap);
  323. va_end(ap);
  324. /* Return number of bytes that would have been written.*/
  325. return retval;
  326. }
  327. /**
  328. * @brief System formatted output function.
  329. * @details This function implements a minimal @p vsnprintf()-like functionality.
  330. * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
  331. * The following parameter types (p) are supported:
  332. * - <b>x</b> hexadecimal integer.
  333. * - <b>X</b> hexadecimal long.
  334. * - <b>o</b> octal integer.
  335. * - <b>O</b> octal long.
  336. * - <b>d</b> decimal signed integer.
  337. * - <b>D</b> decimal signed long.
  338. * - <b>u</b> decimal unsigned integer.
  339. * - <b>U</b> decimal unsigned long.
  340. * - <b>c</b> character.
  341. * - <b>s</b> string.
  342. * .
  343. * @post @p str is NUL-terminated, unless @p size is 0.
  344. *
  345. * @param[in] str pointer to a buffer
  346. * @param[in] size maximum size of the buffer
  347. * @param[in] fmt formatting string
  348. * @param[in] ap list of parameters
  349. * @return The number of characters (excluding the
  350. * terminating NUL byte) that would have been
  351. * stored in @p str if there was room.
  352. *
  353. * @api
  354. */
  355. int chvsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
  356. MemoryStream ms;
  357. BaseSequentialStream *chp;
  358. size_t size_wo_nul;
  359. int retval;
  360. if (size > 0)
  361. size_wo_nul = size - 1;
  362. else
  363. size_wo_nul = 0;
  364. /* Memory stream object to be used as a string writer, reserving one
  365. byte for the final zero.*/
  366. msObjectInit(&ms, (uint8_t *)str, size_wo_nul, 0);
  367. /* Performing the print operation using the common code.*/
  368. chp = (BaseSequentialStream *)(void *)&ms;
  369. retval = chvprintf(chp, fmt, ap);
  370. /* Terminate with a zero, unless size==0.*/
  371. if (ms.eos < size) {
  372. str[ms.eos] = 0;
  373. }
  374. /* Return number of bytes that would have been written.*/
  375. return retval;
  376. }
  377. /** @} */