PageRenderTime 43ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/stmhal/printf.c

https://github.com/jtbattle/micropython
C | 299 lines | 234 code | 25 blank | 40 comment | 56 complexity | a8bfac45a73e03524e9e963d776b158f MD5 | raw file
  1. /*
  2. * This file is part of the Micro Python project, http://micropython.org/
  3. *
  4. * The MIT License (MIT)
  5. *
  6. * Copyright (c) 2013, 2014 Damien P. George
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24. * THE SOFTWARE.
  25. */
  26. #include <stdint.h>
  27. #include <string.h>
  28. #include <stdarg.h>
  29. #include "std.h"
  30. #include "misc.h"
  31. #include "systick.h"
  32. #include "mpconfig.h"
  33. #include "qstr.h"
  34. #include "obj.h"
  35. #include "pfenv.h"
  36. #if 0
  37. #include "lcd.h"
  38. #endif
  39. #include "uart.h"
  40. #include "usb.h"
  41. #if MICROPY_PY_BUILTINS_FLOAT
  42. #include "formatfloat.h"
  43. #endif
  44. void pfenv_prints(const pfenv_t *pfenv, const char *str) {
  45. pfenv->print_strn(pfenv->data, str, strlen(str));
  46. }
  47. int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) {
  48. int chrs = 0;
  49. for (;;) {
  50. {
  51. const char *f = fmt;
  52. while (*f != '\0' && *f != '%') {
  53. ++f; // XXX UTF8 advance char
  54. }
  55. if (f > fmt) {
  56. pfenv->print_strn(pfenv->data, fmt, f - fmt);
  57. chrs += f - fmt;
  58. fmt = f;
  59. }
  60. }
  61. if (*fmt == '\0') {
  62. break;
  63. }
  64. // move past % character
  65. ++fmt;
  66. // parse flags, if they exist
  67. int flags = 0;
  68. char fill = ' ';
  69. while (*fmt != '\0') {
  70. if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST;
  71. else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN;
  72. else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN;
  73. else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ;
  74. else if (*fmt == '0') {
  75. flags |= PF_FLAG_PAD_AFTER_SIGN;
  76. fill = '0';
  77. } else break;
  78. ++fmt;
  79. }
  80. // parse width, if it exists
  81. int width = 0;
  82. for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
  83. width = width * 10 + *fmt - '0';
  84. }
  85. // parse precision, if it exists
  86. int prec = -1;
  87. if (*fmt == '.') {
  88. ++fmt;
  89. if (*fmt == '*') {
  90. ++fmt;
  91. prec = va_arg(args, int);
  92. } else {
  93. prec = 0;
  94. for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
  95. prec = prec * 10 + *fmt - '0';
  96. }
  97. }
  98. if (prec < 0) {
  99. prec = 0;
  100. }
  101. }
  102. // parse long specifiers (current not used)
  103. //bool long_arg = false;
  104. if (*fmt == 'l') {
  105. ++fmt;
  106. //long_arg = true;
  107. }
  108. if (*fmt == '\0') {
  109. break;
  110. }
  111. switch (*fmt) {
  112. case 'b':
  113. if (va_arg(args, int)) {
  114. chrs += pfenv_print_strn(pfenv, "true", 4, flags, fill, width);
  115. } else {
  116. chrs += pfenv_print_strn(pfenv, "false", 5, flags, fill, width);
  117. }
  118. break;
  119. case 'c':
  120. {
  121. char str = va_arg(args, int);
  122. chrs += pfenv_print_strn(pfenv, &str, 1, flags, fill, width);
  123. break;
  124. }
  125. case 's':
  126. {
  127. const char *str = va_arg(args, const char*);
  128. if (str) {
  129. if (prec < 0) {
  130. prec = strlen(str);
  131. }
  132. chrs += pfenv_print_strn(pfenv, str, prec, flags, fill, width);
  133. } else {
  134. chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, fill, width);
  135. }
  136. break;
  137. }
  138. case 'u':
  139. chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, fill, width);
  140. break;
  141. case 'd':
  142. chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, fill, width);
  143. break;
  144. case 'x':
  145. case 'p': // ?
  146. chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, fill, width);
  147. break;
  148. case 'X':
  149. case 'P': // ?
  150. chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, fill, width);
  151. break;
  152. #if MICROPY_PY_BUILTINS_FLOAT
  153. case 'e':
  154. case 'E':
  155. case 'f':
  156. case 'F':
  157. case 'g':
  158. case 'G':
  159. {
  160. #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
  161. mp_float_t f = va_arg(args, double);
  162. chrs += pfenv_print_float(pfenv, f, *fmt, flags, fill, width, prec);
  163. #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
  164. // Currently pfenv_print_float uses snprintf, so if you want
  165. // to use pfenv_print_float with doubles then you'll need
  166. // fix it to not use snprintf first. Otherwise you'll have
  167. // inifinite recursion.
  168. #error Calling pfenv_print_float with double not supported from within printf
  169. #else
  170. #error Unknown MICROPY FLOAT IMPL
  171. #endif
  172. break;
  173. }
  174. #endif
  175. default:
  176. pfenv->print_strn(pfenv->data, fmt, 1);
  177. chrs += 1;
  178. break;
  179. }
  180. ++fmt;
  181. }
  182. return chrs;
  183. }
  184. void stdout_print_strn(void *data, const char *str, unsigned int len) {
  185. // send stdout to UART, USB CDC VCP, and LCD if nothing else
  186. bool any = false;
  187. if (pyb_uart_global_debug != PYB_UART_NONE) {
  188. uart_tx_strn_cooked(pyb_uart_global_debug, str, len);
  189. any = true;
  190. }
  191. if (usb_vcp_is_enabled()) {
  192. usb_vcp_send_strn_cooked(str, len);
  193. any = true;
  194. }
  195. if (!any) {
  196. #if 0
  197. #if MICROPY_HW_HAS_LCD
  198. lcd_print_strn(str, len);
  199. #endif
  200. #endif
  201. }
  202. }
  203. static const pfenv_t pfenv_stdout = {0, stdout_print_strn};
  204. int printf(const char *fmt, ...) {
  205. va_list ap;
  206. va_start(ap, fmt);
  207. int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
  208. va_end(ap);
  209. return ret;
  210. }
  211. int vprintf(const char *fmt, va_list ap) {
  212. return pfenv_printf(&pfenv_stdout, fmt, ap);
  213. }
  214. #if MICROPY_DEBUG_PRINTERS
  215. int DEBUG_printf(const char *fmt, ...) {
  216. (void)stream;
  217. va_list ap;
  218. va_start(ap, fmt);
  219. int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
  220. va_end(ap);
  221. return ret;
  222. }
  223. #endif
  224. // need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a')
  225. int putchar(int c) {
  226. char chr = c;
  227. stdout_print_strn(0, &chr, 1);
  228. return chr;
  229. }
  230. // need this because gcc optimises printf("string\n") -> puts("string")
  231. int puts(const char *s) {
  232. stdout_print_strn(0, s, strlen(s));
  233. char chr = '\n';
  234. stdout_print_strn(0, &chr, 1);
  235. return 1;
  236. }
  237. typedef struct _strn_pfenv_t {
  238. char *cur;
  239. size_t remain;
  240. } strn_pfenv_t;
  241. void strn_print_strn(void *data, const char *str, unsigned int len) {
  242. strn_pfenv_t *strn_pfenv = data;
  243. if (len > strn_pfenv->remain) {
  244. len = strn_pfenv->remain;
  245. }
  246. memcpy(strn_pfenv->cur, str, len);
  247. strn_pfenv->cur += len;
  248. strn_pfenv->remain -= len;
  249. }
  250. int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
  251. strn_pfenv_t strn_pfenv;
  252. strn_pfenv.cur = str;
  253. strn_pfenv.remain = size;
  254. pfenv_t pfenv;
  255. pfenv.data = &strn_pfenv;
  256. pfenv.print_strn = strn_print_strn;
  257. int len = pfenv_printf(&pfenv, fmt, ap);
  258. // add terminating null byte
  259. if (size > 0) {
  260. if (strn_pfenv.remain == 0) {
  261. strn_pfenv.cur[-1] = 0;
  262. } else {
  263. strn_pfenv.cur[0] = 0;
  264. }
  265. }
  266. return len;
  267. }
  268. int snprintf(char *str, size_t size, const char *fmt, ...) {
  269. va_list ap;
  270. va_start(ap, fmt);
  271. int ret = vsnprintf(str, size, fmt, ap);
  272. va_end(ap);
  273. return ret;
  274. }