1 /*********************************************************************
2 *                    SEGGER Microcontroller GmbH                     *
3 *                        The Embedded Experts                        *
4 **********************************************************************
5 *                                                                    *
6 *            (c) 1995 - 2021 SEGGER Microcontroller GmbH             *
7 *                                                                    *
8 *       www.segger.com     Support: support@segger.com               *
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 *                                                                    *
45 *       RTT version: 7.22                                           *
46 *                                                                    *
47 **********************************************************************
48 
49 ---------------------------END-OF-HEADER------------------------------
50 File    : SEGGER_RTT_printf.c
51 Purpose : Replacement for printf to write formatted data via RTT
52 Revision: $Rev: 17697 $
53 ----------------------------------------------------------------------
54 */
55 #include "SEGGER_RTT.h"
56 #include "SEGGER_RTT_Conf.h"
57 
58 /*********************************************************************
59  *
60  *       Defines, configurable
61  *
62  **********************************************************************
63  */
64 
65 #ifndef SEGGER_RTT_PRINTF_BUFFER_SIZE
66 #define SEGGER_RTT_PRINTF_BUFFER_SIZE (64)
67 #endif
68 
69 #include <stdlib.h>
70 #include <stdarg.h>
71 
72 #define FORMAT_FLAG_LEFT_JUSTIFY (1u << 0)
73 #define FORMAT_FLAG_PAD_ZERO     (1u << 1)
74 #define FORMAT_FLAG_PRINT_SIGN   (1u << 2)
75 #define FORMAT_FLAG_ALTERNATE    (1u << 3)
76 
77 /*********************************************************************
78  *
79  *       Types
80  *
81  **********************************************************************
82  */
83 
84 typedef struct
85 {
86     char *pBuffer;
87     unsigned BufferSize;
88     unsigned Cnt;
89 
90     int ReturnValue;
91 
92     unsigned RTTBufferIndex;
93 } SEGGER_RTT_PRINTF_DESC;
94 
95 /*********************************************************************
96  *
97  *       Function prototypes
98  *
99  **********************************************************************
100  */
101 
102 /*********************************************************************
103  *
104  *       Static code
105  *
106  **********************************************************************
107  */
108 /*********************************************************************
109  *
110  *       _StoreChar
111  */
_StoreChar(SEGGER_RTT_PRINTF_DESC * p,char c)112 static void _StoreChar(SEGGER_RTT_PRINTF_DESC *p, char c)
113 {
114     unsigned Cnt;
115 
116     Cnt = p->Cnt;
117     if ((Cnt + 1u) <= p->BufferSize)
118     {
119         *(p->pBuffer + Cnt) = c;
120         p->Cnt              = Cnt + 1u;
121         p->ReturnValue++;
122     }
123     //
124     // Write part of string, when the buffer is full
125     //
126     if (p->Cnt == p->BufferSize)
127     {
128         if (SEGGER_RTT_Write(p->RTTBufferIndex, p->pBuffer, p->Cnt) != p->Cnt)
129         {
130             p->ReturnValue = -1;
131         }
132         else
133         {
134             p->Cnt = 0u;
135         }
136     }
137 }
138 
139 /*********************************************************************
140  *
141  *       _PrintUnsigned
142  */
_PrintUnsigned(SEGGER_RTT_PRINTF_DESC * pBufferDesc,unsigned v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)143 static void _PrintUnsigned(SEGGER_RTT_PRINTF_DESC *pBufferDesc,
144                            unsigned v,
145                            unsigned Base,
146                            unsigned NumDigits,
147                            unsigned FieldWidth,
148                            unsigned FormatFlags)
149 {
150     static const char _aV2C[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
151     unsigned Div;
152     unsigned Digit;
153     unsigned Number;
154     unsigned Width;
155     char c;
156 
157     Number = v;
158     Digit  = 1u;
159     //
160     // Get actual field width
161     //
162     Width = 1u;
163     while (Number >= Base)
164     {
165         Number = (Number / Base);
166         Width++;
167     }
168     if (NumDigits > Width)
169     {
170         Width = NumDigits;
171     }
172     //
173     // Print leading chars if necessary
174     //
175     if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u)
176     {
177         if (FieldWidth != 0u)
178         {
179             if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) && (NumDigits == 0u))
180             {
181                 c = '0';
182             }
183             else
184             {
185                 c = ' ';
186             }
187             while ((FieldWidth != 0u) && (Width < FieldWidth))
188             {
189                 FieldWidth--;
190                 _StoreChar(pBufferDesc, c);
191                 if (pBufferDesc->ReturnValue < 0)
192                 {
193                     break;
194                 }
195             }
196         }
197     }
198     if (pBufferDesc->ReturnValue >= 0)
199     {
200         //
201         // Compute Digit.
202         // Loop until Digit has the value of the highest digit required.
203         // Example: If the output is 345 (Base 10), loop 2 times until Digit is 100.
204         //
205         while (1)
206         {
207             if (NumDigits > 1u)
208             { // User specified a min number of digits to print? => Make sure we loop at least that often, before
209               // checking anything else (> 1 check avoids problems with NumDigits being signed / unsigned)
210                 NumDigits--;
211             }
212             else
213             {
214                 Div = v / Digit;
215                 if (Div < Base)
216                 { // Is our divider big enough to extract the highest digit from value? => Done
217                     break;
218                 }
219             }
220             Digit *= Base;
221         }
222         //
223         // Output digits
224         //
225         do
226         {
227             Div = v / Digit;
228             v -= Div * Digit;
229             _StoreChar(pBufferDesc, _aV2C[Div]);
230             if (pBufferDesc->ReturnValue < 0)
231             {
232                 break;
233             }
234             Digit /= Base;
235         } while (Digit);
236         //
237         // Print trailing spaces if necessary
238         //
239         if ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == FORMAT_FLAG_LEFT_JUSTIFY)
240         {
241             if (FieldWidth != 0u)
242             {
243                 while ((FieldWidth != 0u) && (Width < FieldWidth))
244                 {
245                     FieldWidth--;
246                     _StoreChar(pBufferDesc, ' ');
247                     if (pBufferDesc->ReturnValue < 0)
248                     {
249                         break;
250                     }
251                 }
252             }
253         }
254     }
255 }
256 
257 /*********************************************************************
258  *
259  *       _PrintInt
260  */
_PrintInt(SEGGER_RTT_PRINTF_DESC * pBufferDesc,int v,unsigned Base,unsigned NumDigits,unsigned FieldWidth,unsigned FormatFlags)261 static void _PrintInt(SEGGER_RTT_PRINTF_DESC *pBufferDesc,
262                       int v,
263                       unsigned Base,
264                       unsigned NumDigits,
265                       unsigned FieldWidth,
266                       unsigned FormatFlags)
267 {
268     unsigned Width;
269     int Number;
270 
271     Number = (v < 0) ? -v : v;
272 
273     //
274     // Get actual field width
275     //
276     Width = 1u;
277     while (Number >= (int)Base)
278     {
279         Number = (Number / (int)Base);
280         Width++;
281     }
282     if (NumDigits > Width)
283     {
284         Width = NumDigits;
285     }
286     if ((FieldWidth > 0u) && ((v < 0) || ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN)))
287     {
288         FieldWidth--;
289     }
290 
291     //
292     // Print leading spaces if necessary
293     //
294     if ((((FormatFlags & FORMAT_FLAG_PAD_ZERO) == 0u) || (NumDigits != 0u)) &&
295         ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u))
296     {
297         if (FieldWidth != 0u)
298         {
299             while ((FieldWidth != 0u) && (Width < FieldWidth))
300             {
301                 FieldWidth--;
302                 _StoreChar(pBufferDesc, ' ');
303                 if (pBufferDesc->ReturnValue < 0)
304                 {
305                     break;
306                 }
307             }
308         }
309     }
310     //
311     // Print sign if necessary
312     //
313     if (pBufferDesc->ReturnValue >= 0)
314     {
315         if (v < 0)
316         {
317             v = -v;
318             _StoreChar(pBufferDesc, '-');
319         }
320         else if ((FormatFlags & FORMAT_FLAG_PRINT_SIGN) == FORMAT_FLAG_PRINT_SIGN)
321         {
322             _StoreChar(pBufferDesc, '+');
323         }
324         else
325         {
326         }
327         if (pBufferDesc->ReturnValue >= 0)
328         {
329             //
330             // Print leading zeros if necessary
331             //
332             if (((FormatFlags & FORMAT_FLAG_PAD_ZERO) == FORMAT_FLAG_PAD_ZERO) &&
333                 ((FormatFlags & FORMAT_FLAG_LEFT_JUSTIFY) == 0u) && (NumDigits == 0u))
334             {
335                 if (FieldWidth != 0u)
336                 {
337                     while ((FieldWidth != 0u) && (Width < FieldWidth))
338                     {
339                         FieldWidth--;
340                         _StoreChar(pBufferDesc, '0');
341                         if (pBufferDesc->ReturnValue < 0)
342                         {
343                             break;
344                         }
345                     }
346                 }
347             }
348             if (pBufferDesc->ReturnValue >= 0)
349             {
350                 //
351                 // Print number without sign
352                 //
353                 _PrintUnsigned(pBufferDesc, (unsigned)v, Base, NumDigits, FieldWidth, FormatFlags);
354             }
355         }
356     }
357 }
358 
359 /*********************************************************************
360  *
361  *       Public code
362  *
363  **********************************************************************
364  */
365 /*********************************************************************
366  *
367  *       SEGGER_RTT_vprintf
368  *
369  *  Function description
370  *    Stores a formatted string in SEGGER RTT control block.
371  *    This data is read by the host.
372  *
373  *  Parameters
374  *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
375  *    sFormat      Pointer to format string
376  *    pParamList   Pointer to the list of arguments for the format string
377  *
378  *  Return values
379  *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
380  *     < 0:  Error
381  */
SEGGER_RTT_vprintf(unsigned BufferIndex,const char * sFormat,va_list * pParamList)382 int SEGGER_RTT_vprintf(unsigned BufferIndex, const char *sFormat, va_list *pParamList)
383 {
384     char c;
385     SEGGER_RTT_PRINTF_DESC BufferDesc;
386     int v;
387     unsigned NumDigits;
388     unsigned FormatFlags;
389     unsigned FieldWidth;
390     char acBuffer[SEGGER_RTT_PRINTF_BUFFER_SIZE];
391 
392     BufferDesc.pBuffer        = acBuffer;
393     BufferDesc.BufferSize     = SEGGER_RTT_PRINTF_BUFFER_SIZE;
394     BufferDesc.Cnt            = 0u;
395     BufferDesc.RTTBufferIndex = BufferIndex;
396     BufferDesc.ReturnValue    = 0;
397 
398     do
399     {
400         c = *sFormat;
401         sFormat++;
402         if (c == 0u)
403         {
404             break;
405         }
406         if (c == '%')
407         {
408             //
409             // Filter out flags
410             //
411             FormatFlags = 0u;
412             v           = 1;
413             do
414             {
415                 c = *sFormat;
416                 switch (c)
417                 {
418                     case '-':
419                         FormatFlags |= FORMAT_FLAG_LEFT_JUSTIFY;
420                         sFormat++;
421                         break;
422                     case '0':
423                         FormatFlags |= FORMAT_FLAG_PAD_ZERO;
424                         sFormat++;
425                         break;
426                     case '+':
427                         FormatFlags |= FORMAT_FLAG_PRINT_SIGN;
428                         sFormat++;
429                         break;
430                     case '#':
431                         FormatFlags |= FORMAT_FLAG_ALTERNATE;
432                         sFormat++;
433                         break;
434                     default:
435                         v = 0;
436                         break;
437                 }
438             } while (v);
439             //
440             // filter out field with
441             //
442             FieldWidth = 0u;
443             do
444             {
445                 c = *sFormat;
446                 if ((c < '0') || (c > '9'))
447                 {
448                     break;
449                 }
450                 sFormat++;
451                 FieldWidth = (FieldWidth * 10u) + ((unsigned)c - '0');
452             } while (1);
453 
454             //
455             // Filter out precision (number of digits to display)
456             //
457             NumDigits = 0u;
458             c         = *sFormat;
459             if (c == '.')
460             {
461                 sFormat++;
462                 do
463                 {
464                     c = *sFormat;
465                     if ((c < '0') || (c > '9'))
466                     {
467                         break;
468                     }
469                     sFormat++;
470                     NumDigits = NumDigits * 10u + ((unsigned)c - '0');
471                 } while (1);
472             }
473             //
474             // Filter out length modifier
475             //
476             c = *sFormat;
477             do
478             {
479                 if ((c == 'l') || (c == 'h'))
480                 {
481                     sFormat++;
482                     c = *sFormat;
483                 }
484                 else
485                 {
486                     break;
487                 }
488             } while (1);
489             //
490             // Handle specifiers
491             //
492             switch (c)
493             {
494                 case 'c':
495                 {
496                     char c0;
497                     v  = va_arg(*pParamList, int);
498                     c0 = (char)v;
499                     _StoreChar(&BufferDesc, c0);
500                     break;
501                 }
502                 case 'd':
503                     v = va_arg(*pParamList, int);
504                     _PrintInt(&BufferDesc, v, 10u, NumDigits, FieldWidth, FormatFlags);
505                     break;
506                 case 'u':
507                     v = va_arg(*pParamList, int);
508                     _PrintUnsigned(&BufferDesc, (unsigned)v, 10u, NumDigits, FieldWidth, FormatFlags);
509                     break;
510                 case 'x':
511                 case 'X':
512                     v = va_arg(*pParamList, int);
513                     _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, NumDigits, FieldWidth, FormatFlags);
514                     break;
515                 case 's':
516                 {
517                     const char *s = va_arg(*pParamList, const char *);
518                     do
519                     {
520                         c = *s;
521                         s++;
522                         if (c == '\0')
523                         {
524                             break;
525                         }
526                         _StoreChar(&BufferDesc, c);
527                     } while (BufferDesc.ReturnValue >= 0);
528                 }
529                 break;
530                 case 'p':
531                     v = va_arg(*pParamList, int);
532                     _PrintUnsigned(&BufferDesc, (unsigned)v, 16u, 8u, 8u, 0u);
533                     break;
534                 case '%':
535                     _StoreChar(&BufferDesc, '%');
536                     break;
537                 default:
538                     break;
539             }
540             sFormat++;
541         }
542         else
543         {
544             _StoreChar(&BufferDesc, c);
545         }
546     } while (BufferDesc.ReturnValue >= 0);
547 
548     if (BufferDesc.ReturnValue > 0)
549     {
550         //
551         // Write remaining data, if any
552         //
553         if (BufferDesc.Cnt != 0u)
554         {
555             SEGGER_RTT_Write(BufferIndex, acBuffer, BufferDesc.Cnt);
556         }
557         BufferDesc.ReturnValue += (int)BufferDesc.Cnt;
558     }
559     return BufferDesc.ReturnValue;
560 }
561 
562 /*********************************************************************
563  *
564  *       SEGGER_RTT_printf
565  *
566  *  Function description
567  *    Stores a formatted string in SEGGER RTT control block.
568  *    This data is read by the host.
569  *
570  *  Parameters
571  *    BufferIndex  Index of "Up"-buffer to be used. (e.g. 0 for "Terminal")
572  *    sFormat      Pointer to format string, followed by the arguments for conversion
573  *
574  *  Return values
575  *    >= 0:  Number of bytes which have been stored in the "Up"-buffer.
576  *     < 0:  Error
577  *
578  *  Notes
579  *    (1) Conversion specifications have following syntax:
580  *          %[flags][FieldWidth][.Precision]ConversionSpecifier
581  *    (2) Supported flags:
582  *          -: Left justify within the field width
583  *          +: Always print sign extension for signed conversions
584  *          0: Pad with 0 instead of spaces. Ignored when using '-'-flag or precision
585  *        Supported conversion specifiers:
586  *          c: Print the argument as one char
587  *          d: Print the argument as a signed integer
588  *          u: Print the argument as an unsigned integer
589  *          x: Print the argument as an hexadecimal integer
590  *          s: Print the string pointed to by the argument
591  *          p: Print the argument as an 8-digit hexadecimal integer. (Argument shall be a pointer to void.)
592  */
SEGGER_RTT_printf(unsigned BufferIndex,const char * sFormat,...)593 int SEGGER_RTT_printf(unsigned BufferIndex, const char *sFormat, ...)
594 {
595     int r;
596     va_list ParamList;
597 
598     va_start(ParamList, sFormat);
599     r = SEGGER_RTT_vprintf(BufferIndex, sFormat, &ParamList);
600     va_end(ParamList);
601     return r;
602 }
603 /*************************** End of file ****************************/
604