blob: 003314e8f5f6c0506fba3605075518950975bc0a [file] [log] [blame]
Paul Sokolovskycb66f412014-07-13 23:07:42 +03001/*
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
27#include <assert.h>
28#include <stdio.h>
29#include <stdint.h>
30#include <string.h>
31#include <stdarg.h>
32
33#include "mpconfig.h"
34#include "misc.h"
35#include "qstr.h"
36#include "obj.h"
37#include "pfenv.h"
38
39#if MICROPY_PY_BUILTINS_FLOAT
40#include "formatfloat.h"
41#endif
42
43int pfenv_vprintf(const pfenv_t *pfenv, const char *fmt, va_list args) {
44 int chrs = 0;
45 for (;;) {
46 {
47 const char *f = fmt;
48 while (*f != '\0' && *f != '%') {
49 ++f; // XXX UTF8 advance char
50 }
51 if (f > fmt) {
52 pfenv->print_strn(pfenv->data, fmt, f - fmt);
53 chrs += f - fmt;
54 fmt = f;
55 }
56 }
57
58 if (*fmt == '\0') {
59 break;
60 }
61
62 // move past % character
63 ++fmt;
64
65 // parse flags, if they exist
66 int flags = 0;
67 char fill = ' ';
68 while (*fmt != '\0') {
69 if (*fmt == '-') flags |= PF_FLAG_LEFT_ADJUST;
70 else if (*fmt == '+') flags |= PF_FLAG_SHOW_SIGN;
71 else if (*fmt == ' ') flags |= PF_FLAG_SPACE_SIGN;
72 else if (*fmt == '!') flags |= PF_FLAG_NO_TRAILZ;
73 else if (*fmt == '0') {
74 flags |= PF_FLAG_PAD_AFTER_SIGN;
75 fill = '0';
76 } else break;
77 ++fmt;
78 }
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
86 // parse precision, if it exists
87 int prec = -1;
88 if (*fmt == '.') {
89 ++fmt;
90 if (*fmt == '*') {
91 ++fmt;
92 prec = va_arg(args, int);
93 } else {
94 prec = 0;
95 for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
96 prec = prec * 10 + *fmt - '0';
97 }
98 }
99 if (prec < 0) {
100 prec = 0;
101 }
102 }
103
104 // parse long specifiers (current not used)
105 //bool long_arg = false;
106 if (*fmt == 'l') {
107 ++fmt;
108 //long_arg = true;
109 }
110
111 if (*fmt == '\0') {
112 break;
113 }
114
115 switch (*fmt) {
116 case 'b':
117 if (va_arg(args, int)) {
118 chrs += pfenv_print_strn(pfenv, "true", 4, flags, fill, width);
119 } else {
120 chrs += pfenv_print_strn(pfenv, "false", 5, flags, fill, width);
121 }
122 break;
123 case 'c':
124 {
125 char str = va_arg(args, int);
126 chrs += pfenv_print_strn(pfenv, &str, 1, flags, fill, width);
127 break;
128 }
129 case 's':
130 {
131 const char *str = va_arg(args, const char*);
132 if (str) {
133 if (prec < 0) {
134 prec = strlen(str);
135 }
136 chrs += pfenv_print_strn(pfenv, str, prec, flags, fill, width);
137 } else {
138 chrs += pfenv_print_strn(pfenv, "(null)", 6, flags, fill, width);
139 }
140 break;
141 }
142 case 'u':
143 chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 10, 'a', flags, fill, width);
144 break;
145 case 'd':
146 chrs += pfenv_print_int(pfenv, va_arg(args, int), 1, 10, '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 case 'X':
153 case 'P': // ?
154 chrs += pfenv_print_int(pfenv, va_arg(args, int), 0, 16, 'A', flags, fill, width);
155 break;
156#if MICROPY_PY_BUILTINS_FLOAT
157 case 'e':
158 case 'E':
159 case 'f':
160 case 'F':
161 case 'g':
162 case 'G':
163 {
164#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
165 mp_float_t f = va_arg(args, double);
166 chrs += pfenv_print_float(pfenv, f, *fmt, flags, fill, width, prec);
167#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
168 // Currently pfenv_print_float uses snprintf, but snprintf
169 // itself may be implemented in terms of pfenv_vprintf() for
170 // some ports. So, for extra caution, this case is handled
171 // with assert below. Note that currently ports which
172 // use MICROPY_FLOAT_IMPL_DOUBLE, don't call pfenv_vprintf()
173 // with float format specifier at all.
174 // TODO: resolve this completely
175 assert(0);
176//#error Calling pfenv_print_float with double not supported from within printf
177#else
178#error Unknown MICROPY FLOAT IMPL
179#endif
180 break;
181 }
182#endif
183 default:
184 pfenv->print_strn(pfenv->data, fmt, 1);
185 chrs += 1;
186 break;
187 }
188 ++fmt;
189 }
190 return chrs;
191}
192
193int pfenv_printf(const pfenv_t *pfenv, const char *fmt, ...) {
194 va_list ap;
195 va_start(ap, fmt);
196 int ret = pfenv_vprintf(pfenv, fmt, ap);
197 va_end(ap);
198 return ret;
199}