xref: /btstack/3rd-party/segger-rtt/SEGGER_RTT_printf.c (revision ce6f85e79d1d141c1b45dfa16b2671762457cbb4)
1 /*********************************************************************
2 *                    SEGGER Microcontroller GmbH                     *
3 *                        The Embedded Experts                        *
4 **********************************************************************
5 *                                                                    *
6 *            (c) 1995 - 2019 SEGGER Microcontroller GmbH             *
7 *                                                                    *
8 *       www.segger.com     Support: [email protected]               *
9 *                                                                    *
10 **********************************************************************
11 *                                                                    *
12 *       SEGGER RTT * Real Time Transfer for embedded targets         *
13 *                                                                    *
14 **********************************************************************
15 *                                                                    *
16 * All rights reserved.                                               *
17 *                                                                    *
18 * SEGGER strongly recommends to not make any changes                 *
19 * to or modify the source code of this software in order to stay     *
20 * compatible with the RTT protocol and J-Link.                       *
21 *                                                                    *
22 * Redistribution and use in source and binary forms, with or         *
23 * without modification, are permitted provided that the following    *
24 * condition is met:                                                  *
25 *                                                                    *
26 * o Redistributions of source code must retain the above copyright   *
27 *   notice, this condition and the following disclaimer.             *
28 *                                                                    *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND             *
30 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,        *
31 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF           *
32 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE           *
33 * DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
34 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR           *
35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT  *
36 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;    *
37 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF      *
38 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT          *
39 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE  *
40 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH   *
41 * DAMAGE.                                                            *
42 *                                                                    *
43 **********************************************************************
44 ---------------------------END-OF-HEADER------------------------------
45 File    : SEGGER_RTT_printf.c
46 Purpose : Replacement for printf to write formatted data via RTT
47 Revision: $Rev: 17697 $
48 ----------------------------------------------------------------------
49 */
50 #include "SEGGER_RTT.h"
51 #include "SEGGER_RTT_Conf.h"
52 
53 /*********************************************************************
54 *
55 *       Defines, configurable
56 *
57 **********************************************************************
58 */
59 
60 #ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
61   #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
62 #endif
63 
64 #include <stdlib.h>
65 #include <stdarg.h>
66 
67 
68 #define FORMAT_FLAG_LEFT_JUSTIFY   (1u << 0)
69 #define FORMAT_FLAG_PAD_ZERO       (1u << 1)
70 #define FORMAT_FLAG_PRINT_SIGN     (1u << 2)
71 #define FORMAT_FLAG_ALTERNATE      (1u << 3)
72 
73 /*********************************************************************
74 *
75 *       Types
76 *
77 **********************************************************************
78 */
79 
80 typedef struct {
81   char*     pBuffer;
82   unsigned  BufferSize;
83   unsigned  Cnt;
84 
85   int   ReturnValue;
86 
87   unsigned RTTBufferIndex;
88 } SEGGER_RTT_PRINTF_DESC;
89 
90 /*********************************************************************
91 *
92 *       Function prototypes
93 *
94 **********************************************************************
95 */
96 
97 /*********************************************************************
98 *
99 *       Static code
100 *
101 **********************************************************************
102 */
103 /*********************************************************************
104 *
105 *       _StoreChar
106 */
_StoreChar(SEGGER_RTT_PRINTF_DESC * p,char c)107 static void _StoreChar(SEGGER_RTT_PRINTF_DESC * p, char c) {
108   unsigned Cnt;
109 
110   Cnt = p->Cnt;
111   if ((Cnt + 1u) <= p->BufferSize) {
112     *(p->pBuffer + Cnt) = c;
113     p->Cnt = Cnt + 1u;
114     p->ReturnValue++;
115   }
116   //
117   // Write part of string, when the buffer is full
118   //
119   if (p->Cnt == p->BufferSize) {
120     if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt) {
121       p->ReturnValue = -1;
122     } else {
123       p->Cnt = 0u;
124     }
125   }
126 }
127 
128 /*********************************************************************
129 *
130 *       _PrintUnsigned
131 */
_PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc,unsigned v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)132 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc, unsigned v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
133   static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
134   unsigned Div;
135   unsigned Digit;
136   unsigned Number;
137   unsigned Width;
138   char c;
139 
140   Number = v;
141   Digit = 1u;
142   //
143   // Get actual field width
144   //
145   Width = 1u;
146   while (Number >= Base) {
147     Number = (Number / Base);
148     Width++;
149   }
150   if (NumDigits > Width) {
151     Width = NumDigits;
152   }
153   //
154   // Print leading chars if necessary
155   //
156   if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) {
157     if (FieldWidth != 0u) {
158       if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u)) {
159         c = '0';
160       } else {
161         c = ' ';
162       }
163       while ((FieldWidth != 0u) && (Width < FieldWidth)) {
164         FieldWidth--;
165         _StoreChar(pBufferDesc, c);
166         if (pBufferDesc->ReturnValue < 0) {
167           break;
168         }
169       }
170     }
171   }
172   if (pBufferDesc->ReturnValue >= 0) {
173     //
174     // Compute Digit.
175     // Loop until Digit has the value of the highest digit required.
176     // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
177     //
178     while (1) {
179       if (NumDigits > 1u) {       // User specified a min number of digits to print? => Make sure we loop at least that often, before checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned)
180         NumDigits--;
181       } else {
182         Div = v / Digit;
183         if (Div < Base) {        // Is our divider big enough to extract the highest digit from value? => Done
184           break;
185         }
186       }
187       Digit *= Base;
188     }
189     //
190     // Output digits
191     //
192     do {
193       Div = v / Digit;
194       v -= Div * Digit;
195       _StoreChar(pBufferDesc, _aV2C[Div]);
196       if (pBufferDesc->ReturnValue < 0) {
197         break;
198       }
199       Digit /= Base;
200     } while (Digit);
201     //
202     // Print trailing spaces if necessary
203     //
204     if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY) {
205       if (FieldWidth != 0u) {
206         while ((FieldWidth != 0u) && (Width < FieldWidth)) {
207           FieldWidth--;
208           _StoreChar(pBufferDesc, ' ');
209           if (pBufferDesc->ReturnValue < 0) {
210             break;
211           }
212         }
213       }
214     }
215   }
216 }
217 
218 /*********************************************************************
219 *
220 *       _PrintInt
221 */
_PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc,int v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)222 static void _PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc, int v, unsigned Base, unsigned NumDigits, unsigned FieldWidth, unsigned FormatFlags) {
223   unsigned Width;
224   int Number;
225 
226   Number = (v < 0) ? -v : v;
227 
228   //
229   // Get actual field width
230   //
231   Width = 1u;
232   while (Number >= (int)Base) {
233     Number = (Number / (int)Base);
234     Width++;
235   }
236   if (NumDigits > Width) {
237     Width = NumDigits;
238   }
239   if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN))) {
240     FieldWidth--;
241   }
242 
243   //
244   // Print leading spaces if necessary
245   //
246   if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)) {
247     if (FieldWidth != 0u) {
248       while ((FieldWidth != 0u) && (Width < FieldWidth)) {
249         FieldWidth--;
250         _StoreChar(pBufferDesc, ' ');
251         if (pBufferDesc->ReturnValue < 0) {
252           break;
253         }
254       }
255     }
256   }
257   //
258   // Print sign if necessary
259   //
260   if (pBufferDesc->ReturnValue >= 0) {
261     if (v < 0) {
262       v = -v;
263       _StoreChar(pBufferDesc, '-');
264     } else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN) {
265       _StoreChar(pBufferDesc, '+');
266     } else {
267 
268     }
269     if (pBufferDesc->ReturnValue >= 0) {
270       //
271       // Print leading zeros if necessary
272       //
273       if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u)) {
274         if (FieldWidth != 0u) {
275           while ((FieldWidth != 0u) && (Width < FieldWidth)) {
276             FieldWidth--;
277             _StoreChar(pBufferDesc, '0');
278             if (pBufferDesc->ReturnValue < 0) {
279               break;
280             }
281           }
282         }
283       }
284       if (pBufferDesc->ReturnValue >= 0) {
285         //
286         // Print number without sign
287         //
288         _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
289       }
290     }
291   }
292 }
293 
294 /*********************************************************************
295 *
296 *       Public code
297 *
298 **********************************************************************
299 */
300 /*********************************************************************
301 *
302 *       SEGGER_RTT_vprintf
303 *
304 *  Function description
305 *    Stores a formatted string in SEGGER RTT control block.
306 *    This data is read by the host.
307 *
308 *  Parameters
309 *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
310 *    sFormat      Pointer to format string
311 *    pParamList   Pointer to the list of arguments for the format string
312 *
313 *  Return values
314 *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
315 *     < 0:  Error
316 */
SEGGER_RTT_vprintf(unsigned BufferIndex,const char * sFormat,va_list * pParamList)317 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char * sFormat, va_list * pParamList) {
318   char c;
319   SEGGER_RTT_PRINTF_DESC BufferDesc;
320   int v;
321   unsigned NumDigits;
322   unsigned FormatFlags;
323   unsigned FieldWidth;
324   char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
325 
326   BufferDesc.pBuffer        = acBuffer;
327   BufferDesc.BufferSize     = SEGGER_RTT_PRINTF_BUFFER_SIZE;
328   BufferDesc.Cnt            = 0u;
329   BufferDesc.RTTBufferIndex = BufferIndex;
330   BufferDesc.ReturnValue    = 0;
331 
332   do {
333     c = *sFormat;
334     sFormat++;
335     if (c == 0u) {
336       break;
337     }
338     if (c == '%') {
339       //
340       // Filter out flags
341       //
342       FormatFlags = 0u;
343       v = 1;
344       do {
345         c = *sFormat;
346         switch (c) {
347         case '-': FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY; sFormat++; break;
348         case '0': FormatFlags |= FORMAT_FLAG_PAD_ZERO;     sFormat++; break;
349         case '+': FormatFlags |= FORMAT_FLAG_PRINT_SIGN;   sFormat++; break;
350         case '#': FormatFlags |= FORMAT_FLAG_ALTERNATE;    sFormat++; break;
351         default:  v = 0; break;
352         }
353       } while (v);
354       //
355       // filter out field with
356       //
357       FieldWidth = 0u;
358       do {
359         c = *sFormat;
360         if ((c < '0') || (c > '9')) {
361           break;
362         }
363         sFormat++;
364         FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
365       } while (1);
366 
367       //
368       // Filter out precision (number of digits to display)
369       //
370       NumDigits = 0u;
371       c = *sFormat;
372       if (c == '.') {
373         sFormat++;
374         do {
375           c = *sFormat;
376           if ((c < '0') || (c > '9')) {
377             break;
378           }
379           sFormat++;
380           NumDigits = NumDigits * 10u + ((unsigned)c - '0');
381         } while (1);
382       }
383       //
384       // Filter out length modifier
385       //
386       c = *sFormat;
387       do {
388         if ((c == 'l') || (c == 'h')) {
389           sFormat++;
390           c = *sFormat;
391         } else {
392           break;
393         }
394       } while (1);
395       //
396       // Handle specifiers
397       //
398       switch (c) {
399       case 'c': {
400         char c0;
401         v = va_arg(*pParamList, int);
402         c0 = (char)v;
403         _StoreChar(&BufferDesc, c0);
404         break;
405       }
406       case 'd':
407         v = va_arg(*pParamList, int);
408         _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
409         break;
410       case 'u':
411         v = va_arg(*pParamList, int);
412         _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
413         break;
414       case 'x':
415       case 'X':
416         v = va_arg(*pParamList, int);
417         _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
418         break;
419       case 's':
420         {
421           const char * s = va_arg(*pParamList, const char *);
422           do {
423             c = *s;
424             s++;
425             if (c == '\0') {
426               break;
427             }
428            _StoreChar(&BufferDesc, c);
429           } while (BufferDesc.ReturnValue >= 0);
430         }
431         break;
432       case 'p':
433         v = va_arg(*pParamList, int);
434         _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
435         break;
436       case '%':
437         _StoreChar(&BufferDesc, '%');
438         break;
439       default:
440         break;
441       }
442       sFormat++;
443     } else {
444       _StoreChar(&BufferDesc, c);
445     }
446   } while (BufferDesc.ReturnValue >= 0);
447 
448   if (BufferDesc.ReturnValue > 0) {
449     //
450     // Write remaining data, if any
451     //
452     if (BufferDesc.Cnt != 0u) {
453       SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
454     }
455     BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
456   }
457   return BufferDesc.ReturnValue;
458 }
459 
460 /*********************************************************************
461 *
462 *       SEGGER_RTT_printf
463 *
464 *  Function description
465 *    Stores a formatted string in SEGGER RTT control block.
466 *    This data is read by the host.
467 *
468 *  Parameters
469 *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
470 *    sFormat      Pointer to format string, followed by the arguments for conversion
471 *
472 *  Return values
473 *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
474 *     < 0:  Error
475 *
476 *  Notes
477 *    (1) Conversion specifications have following syntax:
478 *          %[flags][FieldWidth][.Precision]ConversionSpecifier
479 *    (2) Supported flags:
480 *          -: Left justify within the field width
481 *          +: Always print sign extension for signed conversions
482 *          0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
483 *        Supported conversion specifiers:
484 *          c: Print the argument as one char
485 *          d: Print the argument as a signed integer
486 *          u: Print the argument as an unsigned integer
487 *          x: Print the argument as an hexadecimal integer
488 *          s: Print the string pointed to by the argument
489 *          p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
490 */
SEGGER_RTT_printf(unsigned BufferIndex,const char * sFormat,...)491 int SEGGER_RTT_printf(unsigned BufferIndex, const char * sFormat, ...) {
492   int r;
493   va_list ParamList;
494 
495   va_start(ParamList, sFormat);
496   r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
497   va_end(ParamList);
498   return r;
499 }
500 /*************************** End of file ****************************/
501