blob: c4731aa88d676e4538518f8ed21b75e83b9f4cb0 [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
Paul Sokolovsky81394942014-06-28 23:32:03 +030031#include "mpconfig.h"
Dave Hylandsf14b92b2014-03-12 18:06:26 -070032#include "std.h"
33#include "misc.h"
34#include "systick.h"
Dave Hylandsf14b92b2014-03-12 18:06:26 -070035#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
Damien Georgefb510b32014-06-01 13:32:54 +010044#if MICROPY_PY_BUILTINS_FLOAT
Dave Hylandsf14b92b2014-03-12 18:06:26 -070045#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;
Damien Georgefb510b32014-06-01 13:32:54 +0100165#if MICROPY_PY_BUILTINS_FLOAT
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700166 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
Damien Georgee79c6692014-06-15 09:10:07 +0100198STATIC void stdout_print_strn(void *data, const char *str, unsigned int len) {
Damien George34ab8dd2014-06-15 00:41:47 +0100199 // TODO this needs to be replaced with a proper stdio interface ala CPython
200 // send stdout to UART and USB CDC VCP
Damien George75337002014-04-21 12:03:09 +0100201 if (pyb_uart_global_debug != PYB_UART_NONE) {
202 uart_tx_strn_cooked(pyb_uart_global_debug, str, len);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700203 }
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700204 if (usb_vcp_is_enabled()) {
205 usb_vcp_send_strn_cooked(str, len);
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700206 }
207}
208
209static const pfenv_t pfenv_stdout = {0, stdout_print_strn};
210
211int printf(const char *fmt, ...) {
212 va_list ap;
213 va_start(ap, fmt);
214 int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
215 va_end(ap);
216 return ret;
217}
218
219int vprintf(const char *fmt, va_list ap) {
220 return pfenv_printf(&pfenv_stdout, fmt, ap);
221}
222
223#if MICROPY_DEBUG_PRINTERS
224int DEBUG_printf(const char *fmt, ...) {
225 (void)stream;
226 va_list ap;
227 va_start(ap, fmt);
228 int ret = pfenv_printf(&pfenv_stdout, fmt, ap);
229 va_end(ap);
230 return ret;
231}
232#endif
233
234// need this because gcc optimises printf("%c", c) -> putchar(c), and printf("a") -> putchar('a')
235int putchar(int c) {
236 char chr = c;
237 stdout_print_strn(0, &chr, 1);
238 return chr;
239}
240
241// need this because gcc optimises printf("string\n") -> puts("string")
242int puts(const char *s) {
243 stdout_print_strn(0, s, strlen(s));
244 char chr = '\n';
245 stdout_print_strn(0, &chr, 1);
246 return 1;
247}
248
249typedef struct _strn_pfenv_t {
250 char *cur;
251 size_t remain;
252} strn_pfenv_t;
253
254void strn_print_strn(void *data, const char *str, unsigned int len) {
255 strn_pfenv_t *strn_pfenv = data;
256 if (len > strn_pfenv->remain) {
257 len = strn_pfenv->remain;
258 }
259 memcpy(strn_pfenv->cur, str, len);
260 strn_pfenv->cur += len;
261 strn_pfenv->remain -= len;
262}
Damien Georged0f9f6c2014-04-17 18:58:09 +0100263
Dave Hylandsf14b92b2014-03-12 18:06:26 -0700264int vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
265 strn_pfenv_t strn_pfenv;
266 strn_pfenv.cur = str;
267 strn_pfenv.remain = size;
268 pfenv_t pfenv;
269 pfenv.data = &strn_pfenv;
270 pfenv.print_strn = strn_print_strn;
271 int len = pfenv_printf(&pfenv, fmt, ap);
272 // add terminating null byte
273 if (size > 0) {
274 if (strn_pfenv.remain == 0) {
275 strn_pfenv.cur[-1] = 0;
276 } else {
277 strn_pfenv.cur[0] = 0;
278 }
279 }
280 return len;
281}
282
283int snprintf(char *str, size_t size, const char *fmt, ...) {
284 va_list ap;
285 va_start(ap, fmt);
286 int ret = vsnprintf(str, size, fmt, ap);
287 va_end(ap);
288 return ret;
289}