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