blob: 3f88a9c5aad2640adac6c080f3193a9d1e8a1e65 [file] [log] [blame]
Damien George04b91472014-05-03 23:27:38 +01001/*
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
Dave Hylandsf14b92b2014-03-12 18:06:26 -070027#include <stdint.h>
28#include <string.h>
29#include <stdarg.h>
30
31#include "std.h"
32#include "misc.h"
33#include "systick.h"
34#include "mpconfig.h"
35#include "qstr.h"
36#include "obj.h"
Dave Hylandsbaf6f142014-03-30 21:06:50 -070037#include "pfenv.h"
Dave Hylandsf14b92b2014-03-12 18:06:26 -070038#if 0
39#include "lcd.h"
40#endif
Damien George75337002014-04-21 12:03:09 +010041#include "uart.h"
Dave Hylandsf14b92b2014-03-12 18:06:26 -070042#include "usb.h"
Dave Hylandsf14b92b2014-03-12 18:06:26 -070043
44#if MICROPY_ENABLE_FLOAT
45#include "formatfloat.h"
46#endif
47
Dave Hylandsf14b92b2014-03-12 18:06:26 -070048void pfenv_prints(const pfenv_t *pfenv, const char *str) {
49 pfenv->print_strn(pfenv->data, str, strlen(str));
50}
51
52int pfenv_printf(const pfenv_t *pfenv, const char *fmt, va_list args) {
53 int chrs = 0;
54 for (;;) {
55 {
56 const char *f = fmt;
57 while (*f != '\0' && *f != '%') {
58 ++f; // XXX UTF8 advance char
59 }
60 if (f > fmt) {
61 pfenv->print_strn(pfenv->data, fmt, f - fmt);
62 chrs += f - fmt;
63 fmt = f;
64 }
65 }
66
67 if (*fmt == '\0') {
68 break;
69 }
70
71 // move past % character
72 ++fmt;
73
74 // parse flags, if they exist
75 int flags = 0;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070076 char fill = ' ';
Dave Hylandsf14b92b2014-03-12 18:06:26 -070077 while (*fmt != '\0') {
78 if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST;
79 else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN;
80 else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN;
81 else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ;
Dave Hylandsbaf6f142014-03-30 21:06:50 -070082 else if (*fmt == '0') {
83 flags |= PF_FLAG_PAD_AFTER_SIGN;
84 fill = '0';
85 } else break;
Dave Hylandsf14b92b2014-03-12 18:06:26 -070086 ++fmt;
87 }
88
89 // parse width, if it exists
90 int width = 0;
91 for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
92 width = width * 10 + *fmt - '0';
93 }
94
95 // parse precision, if it exists
96 int prec = -1;
97 if (*fmt == '.') {
98 ++fmt;
99 if (*fmt == '*') {
100 ++fmt;
101 prec = va_arg(args, int);
102 } else {
103 prec = 0;
104 for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
105 prec = prec * 10 + *fmt - '0';
106 }
107 }
108 if (prec < 0) {
109 prec = 0;
110 }
111 }
112
113 // parse long specifiers (current not used)
114 //bool long_arg = false;
115 if (*fmt == 'l') {
116 ++fmt;
117 //long_arg = true;
118 }
119
120 if (*fmt == '\0') {
121 break;
122 }
123
124 switch (*fmt) {
125 case 'b':
126 if (va_arg(args, int)) {
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700127 chrs += pfenv_print_strn(pfenv, "true", 4, flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700128 } else {
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700129 chrs += pfenv_print_strn(pfenv, "false", 5, flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700130 }
131 break;
132 case 'c':
133 {
134 char str = va_arg(args, int);
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700135 chrs += pfenv_print_strn(pfenv, &str, 1, flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700136 break;
137 }
138 case 's':
139 {
140 const char *str = va_arg(args, const char*);
141 if (str) {
142 if (prec < 0) {
143 prec = strlen(str);
144 }
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700145 chrs += pfenv_print_strn(pfenv, str, prec, flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700146 } else {
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700147 chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700148 }
149 break;
150 }
151 case 'u':
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700152 chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700153 break;
154 case 'd':
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700155 chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, 'a', flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700156 break;
157 case 'x':
158 case 'p': // ?
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700159 chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'a', flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700160 break;
161 case 'X':
162 case 'P': // ?
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700163 chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, fill, width);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700164 break;
165#if MICROPY_ENABLE_FLOAT
166 case 'e':
167 case 'E':
168 case 'f':
169 case 'F':
170 case 'g':
171 case 'G':
172 {
Dave Hylandsbaf6f142014-03-30 21:06:50 -0700173#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
174 mp_float_t f = va_arg(args, double);
175 chrs += pfenv_print_float(pfenv, f, *fmt, flags, fill, width, prec);
176#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
177 // Currently pfenv_print_float uses snprintf, so if you want
178 // to use pfenv_print_float with doubles then you'll need
179 // fix it to not use snprintf first. Otherwise you'll have
180 // inifinite recursion.
181#error Calling pfenv_print_float with double not supported from within printf
182#else
183#error Unknown MICROPY FLOAT IMPL
184#endif
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700185 break;
186 }
187#endif
188 default:
189 pfenv->print_strn(pfenv->data, fmt, 1);
190 chrs += 1;
191 break;
192 }
193 ++fmt;
194 }
195 return chrs;
196}
197
198void stdout_print_strn(void *data, const char *str, unsigned int len) {
Damien George75337002014-04-21 12:03:09 +0100199 // send stdout to UART, USB CDC VCP, and LCD if nothing else
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700200 bool any = false;
201
Damien George75337002014-04-21 12:03:09 +0100202 if (pyb_uart_global_debug != PYB_UART_NONE) {
203 uart_tx_strn_cooked(pyb_uart_global_debug, str, len);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700204 any = true;
205 }
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700206 if (usb_vcp_is_enabled()) {
207 usb_vcp_send_strn_cooked(str, len);
208 any = true;
209 }
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700210 if (!any) {
211#if 0
212#if MICROPY_HW_HAS_LCD
213 lcd_print_strn(str, len);
214#endif
215#endif
216 }
217}
218
219static const pfenv_t pfenv_stdout = {0, stdout_print_strn};
220
221int printf(const char *fmt, ...) {
222 va_list ap;
223 va_start(ap, fmt);
224 int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
225 va_end(ap);
226 return ret;
227}
228
229int vprintf(const char *fmt, va_list ap) {
230 return pfenv_printf(&pfenv_stdout, fmt, ap);
231}
232
233#if MICROPY_DEBUG_PRINTERS
234int DEBUG_printf(const char *fmt, ...) {
235 (void)stream;
236 va_list ap;
237 va_start(ap, fmt);
238 int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
239 va_end(ap);
240 return ret;
241}
242#endif
243
244// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a')
245int putchar(int c) {
246 char chr = c;
247 stdout_print_strn(0, &chr, 1);
248 return chr;
249}
250
251// need this because gcc optimises printf("string\n") -> puts("string")
252int puts(const char *s) {
253 stdout_print_strn(0, s, strlen(s));
254 char chr = '\n';
255 stdout_print_strn(0, &chr, 1);
256 return 1;
257}
258
259typedef struct _strn_pfenv_t {
260 char *cur;
261 size_t remain;
262} strn_pfenv_t;
263
264void strn_print_strn(void *data, const char *str, unsigned int len) {
265 strn_pfenv_t *strn_pfenv = data;
266 if (len > strn_pfenv->remain) {
267 len = strn_pfenv->remain;
268 }
269 memcpy(strn_pfenv->cur, str, len);
270 strn_pfenv->cur += len;
271 strn_pfenv->remain -= len;
272}
Damien Georged0f9f6c2014-04-17 18:58:09 +0100273
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700274int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
275 strn_pfenv_t strn_pfenv;
276 strn_pfenv.cur = str;
277 strn_pfenv.remain = size;
278 pfenv_t pfenv;
279 pfenv.data = &strn_pfenv;
280 pfenv.print_strn = strn_print_strn;
281 int len = pfenv_printf(&pfenv, fmt, ap);
282 // add terminating null byte
283 if (size > 0) {
284 if (strn_pfenv.remain == 0) {
285 strn_pfenv.cur[-1] = 0;
286 } else {
287 strn_pfenv.cur[0] = 0;
288 }
289 }
290 return len;
291}
292
293int snprintf(char *str, size_t size, const char *fmt, ...) {
294 va_list ap;
295 va_start(ap, fmt);
296 int ret = vsnprintf(str, size, fmt, ap);
297 va_end(ap);
298 return ret;
299}