ChibiOS/HAL  7.0.3
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 
38 static 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  *--q = i;
60  l /= radix;
61  } while ((ll /= radix) != 0);
62 
63  i = (int)(p + MAX_FILLER - q);
64  do
65  *p++ = *q++;
66  while (--i);
67 
68  return p;
69 }
70 
71 static char *ch_ltoa(char *p, long num, unsigned radix) {
72 
73  return long_to_string_with_divisor(p, num, radix, 0);
74 }
75 
76 #if CHPRINTF_USE_FLOAT
77 static const long pow10[FLOAT_PRECISION] = {
78  10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
79 };
80 
81 static char *ftoa(char *p, double num, unsigned long precision) {
82  long l;
83 
84  if ((precision == 0) || (precision > FLOAT_PRECISION))
85  precision = FLOAT_PRECISION;
86  precision = pow10[precision - 1];
87 
88  l = (long)num;
89  p = long_to_string_with_divisor(p, l, 10, 0);
90  *p++ = '.';
91  l = (long)((num - l) * precision);
92  return long_to_string_with_divisor(p, l, 10, precision / 10);
93 }
94 #endif
95 
96 /**
97  * @brief System formatted output function.
98  * @details This function implements a minimal @p vprintf()-like functionality
99  * with output on a @p BaseSequentialStream.
100  * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
101  * The following parameter types (p) are supported:
102  * - <b>x</b> hexadecimal integer.
103  * - <b>X</b> hexadecimal long.
104  * - <b>o</b> octal integer.
105  * - <b>O</b> octal long.
106  * - <b>d</b> decimal signed integer.
107  * - <b>D</b> decimal signed long.
108  * - <b>u</b> decimal unsigned integer.
109  * - <b>U</b> decimal unsigned long.
110  * - <b>c</b> character.
111  * - <b>s</b> string.
112  * .
113  *
114  * @param[in] chp pointer to a @p BaseSequentialStream implementing object
115  * @param[in] fmt formatting string
116  * @param[in] ap list of parameters
117  * @return The number of bytes that would have been
118  * written to @p chp if no stream error occurs
119  *
120  * @api
121  */
122 int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap) {
123  char *p, *s, c, filler;
124  int i, precision, width;
125  int n = 0;
126  bool is_long, left_align;
127  long l;
128 #if CHPRINTF_USE_FLOAT
129  float f;
130  char tmpbuf[2*MAX_FILLER + 1];
131 #else
132  char tmpbuf[MAX_FILLER + 1];
133 #endif
134 
135  while (true) {
136  c = *fmt++;
137  if (c == 0)
138  return n;
139  if (c != '%') {
140  streamPut(chp, (uint8_t)c);
141  n++;
142  continue;
143  }
144  p = tmpbuf;
145  s = tmpbuf;
146  left_align = FALSE;
147  if (*fmt == '-') {
148  fmt++;
149  left_align = TRUE;
150  }
151  filler = ' ';
152  if (*fmt == '0') {
153  fmt++;
154  filler = '0';
155  }
156  width = 0;
157  while (TRUE) {
158  c = *fmt++;
159  if (c >= '0' && c <= '9')
160  c -= '0';
161  else if (c == '*')
162  c = va_arg(ap, int);
163  else
164  break;
165  width = width * 10 + c;
166  }
167  precision = 0;
168  if (c == '.') {
169  while (TRUE) {
170  c = *fmt++;
171  if (c >= '0' && c <= '9')
172  c -= '0';
173  else if (c == '*')
174  c = va_arg(ap, int);
175  else
176  break;
177  precision *= 10;
178  precision += c;
179  }
180  }
181  /* Long modifier.*/
182  if (c == 'l' || c == 'L') {
183  is_long = TRUE;
184  if (*fmt)
185  c = *fmt++;
186  }
187  else
188  is_long = (c >= 'A') && (c <= 'Z');
189 
190  /* Command decoding.*/
191  switch (c) {
192  case 'c':
193  filler = ' ';
194  *p++ = va_arg(ap, int);
195  break;
196  case 's':
197  filler = ' ';
198  if ((s = va_arg(ap, char *)) == 0)
199  s = "(null)";
200  if (precision == 0)
201  precision = 32767;
202  for (p = s; *p && (--precision >= 0); p++)
203  ;
204  break;
205  case 'D':
206  case 'd':
207  case 'I':
208  case 'i':
209  if (is_long)
210  l = va_arg(ap, long);
211  else
212  l = va_arg(ap, int);
213  if (l < 0) {
214  *p++ = '-';
215  l = -l;
216  }
217  p = ch_ltoa(p, l, 10);
218  break;
219 #if CHPRINTF_USE_FLOAT
220  case 'f':
221  f = (float) va_arg(ap, double);
222  if (f < 0) {
223  *p++ = '-';
224  f = -f;
225  }
226  p = ftoa(p, f, precision);
227  break;
228 #endif
229  case 'X':
230  case 'x':
231  c = 16;
232  goto unsigned_common;
233  case 'U':
234  case 'u':
235  c = 10;
236  goto unsigned_common;
237  case 'O':
238  case 'o':
239  c = 8;
240 unsigned_common:
241  if (is_long)
242  l = va_arg(ap, unsigned long);
243  else
244  l = va_arg(ap, unsigned int);
245  p = ch_ltoa(p, l, c);
246  break;
247  default:
248  *p++ = c;
249  break;
250  }
251  i = (int)(p - s);
252  if ((width -= i) < 0)
253  width = 0;
254  if (left_align == FALSE)
255  width = -width;
256  if (width < 0) {
257  if (*s == '-' && filler == '0') {
258  streamPut(chp, (uint8_t)*s++);
259  n++;
260  i--;
261  }
262  do {
263  streamPut(chp, (uint8_t)filler);
264  n++;
265  } while (++width != 0);
266  }
267  while (--i >= 0) {
268  streamPut(chp, (uint8_t)*s++);
269  n++;
270  }
271 
272  while (width) {
273  streamPut(chp, (uint8_t)filler);
274  n++;
275  width--;
276  }
277  }
278 }
279 
280 /**
281  * @brief System formatted output function.
282  * @details This function implements a minimal @p printf() like functionality
283  * with output on a @p BaseSequentialStream.
284  * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
285  * The following parameter types (p) are supported:
286  * - <b>x</b> hexadecimal integer.
287  * - <b>X</b> hexadecimal long.
288  * - <b>o</b> octal integer.
289  * - <b>O</b> octal long.
290  * - <b>d</b> decimal signed integer.
291  * - <b>D</b> decimal signed long.
292  * - <b>u</b> decimal unsigned integer.
293  * - <b>U</b> decimal unsigned long.
294  * - <b>c</b> character.
295  * - <b>s</b> string.
296  * .
297  *
298  * @param[in] chp pointer to a @p BaseSequentialStream implementing object
299  * @param[in] fmt formatting string
300  * @return The number of bytes that would have been
301  * written to @p chp if no stream error occurs
302  *
303  * @api
304  */
305 int chprintf(BaseSequentialStream *chp, const char *fmt, ...) {
306  va_list ap;
307  int formatted_bytes;
308 
309  va_start(ap, fmt);
310  formatted_bytes = chvprintf(chp, fmt, ap);
311  va_end(ap);
312 
313  return formatted_bytes;
314 }
315 
316 /**
317  * @brief System formatted output function.
318  * @details This function implements a minimal @p snprintf()-like functionality.
319  * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
320  * The following parameter types (p) are supported:
321  * - <b>x</b> hexadecimal integer.
322  * - <b>X</b> hexadecimal long.
323  * - <b>o</b> octal integer.
324  * - <b>O</b> octal long.
325  * - <b>d</b> decimal signed integer.
326  * - <b>D</b> decimal signed long.
327  * - <b>u</b> decimal unsigned integer.
328  * - <b>U</b> decimal unsigned long.
329  * - <b>c</b> character.
330  * - <b>s</b> string.
331  * .
332  * @post @p str is NUL-terminated, unless @p size is 0.
333  *
334  * @param[in] str pointer to a buffer
335  * @param[in] size maximum size of the buffer
336  * @param[in] fmt formatting string
337  * @return The number of characters (excluding the
338  * terminating NUL byte) that would have been
339  * stored in @p str if there was room.
340  *
341  * @api
342  */
343 int chsnprintf(char *str, size_t size, const char *fmt, ...) {
344  va_list ap;
345  int retval;
346 
347  /* Performing the print operation.*/
348  va_start(ap, fmt);
349  retval = chvsnprintf(str, size, fmt, ap);
350  va_end(ap);
351 
352  /* Return number of bytes that would have been written.*/
353  return retval;
354 }
355 
356 /**
357  * @brief System formatted output function.
358  * @details This function implements a minimal @p vsnprintf()-like functionality.
359  * The general parameters format is: %[-][width|*][.precision|*][l|L]p.
360  * The following parameter types (p) are supported:
361  * - <b>x</b> hexadecimal integer.
362  * - <b>X</b> hexadecimal long.
363  * - <b>o</b> octal integer.
364  * - <b>O</b> octal long.
365  * - <b>d</b> decimal signed integer.
366  * - <b>D</b> decimal signed long.
367  * - <b>u</b> decimal unsigned integer.
368  * - <b>U</b> decimal unsigned long.
369  * - <b>c</b> character.
370  * - <b>s</b> string.
371  * .
372  * @post @p str is NUL-terminated, unless @p size is 0.
373  *
374  * @param[in] str pointer to a buffer
375  * @param[in] size maximum size of the buffer
376  * @param[in] fmt formatting string
377  * @param[in] ap list of parameters
378  * @return The number of characters (excluding the
379  * terminating NUL byte) that would have been
380  * stored in @p str if there was room.
381  *
382  * @api
383  */
384 int chvsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
385  MemoryStream ms;
387  size_t size_wo_nul;
388  int retval;
389 
390  if (size > 0)
391  size_wo_nul = size - 1;
392  else
393  size_wo_nul = 0;
394 
395  /* Memory stream object to be used as a string writer, reserving one
396  byte for the final zero.*/
397  msObjectInit(&ms, (uint8_t *)str, size_wo_nul, 0);
398 
399  /* Performing the print operation using the common code.*/
400  chp = (BaseSequentialStream *)(void *)&ms;
401  retval = chvprintf(chp, fmt, ap);
402 
403  /* Terminate with a zero, unless size==0.*/
404  if (ms.eos < size) {
405  str[ms.eos] = 0;
406  }
407 
408  /* Return number of bytes that would have been written.*/
409  return retval;
410 }
411 
412 /** @} */
Base stream class.
Definition: hal_streams.h:83
#define streamPut(ip, b)
Sequential Stream blocking byte write.
Definition: hal_streams.h:137
HAL subsystem header.
void msObjectInit(MemoryStream *msp, uint8_t *buffer, size_t size, size_t eos)
Memory stream object initialization.
Definition: memstreams.c:104
int chsnprintf(char *str, size_t size, const char *fmt,...)
System formatted output function.
Definition: chprintf.c:343
Mini printf-like functionality.
Memory stream object.
Definition: memstreams.h:70
int chvsnprintf(char *str, size_t size, const char *fmt, va_list ap)
System formatted output function.
Definition: chprintf.c:384
Memory streams structures and macros.
int chprintf(BaseSequentialStream *chp, const char *fmt,...)
System formatted output function.
Definition: chprintf.c:305
int chvprintf(BaseSequentialStream *chp, const char *fmt, va_list ap)
System formatted output function.
Definition: chprintf.c:122