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