blob: 6df137725a4e4a783b7779b14408b2696c85a4bf [file] [log] [blame]
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +01001/*
2 * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <debug.h>
8#include <platform.h>
9#include <stdarg.h>
10
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010011static void string_print(char **s, size_t n, size_t *chars_printed,
12 const char *str)
13{
14 while (*str) {
15 if (*chars_printed < n)
16 *(*s)++ = *str;
17 (*chars_printed)++;
18 str++;
19 }
20}
21
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010022static void unsigned_dec_print(char **s, size_t n, size_t *chars_printed,
23 unsigned int unum)
24{
25 /* Enough for a 32-bit unsigned decimal integer (4294967295). */
26 unsigned char num_buf[10];
27 int i = 0, rem;
28
29 do {
30 rem = unum % 10;
31 num_buf[i++] = '0' + rem;
32 } while (unum /= 10);
33
34 while (--i >= 0) {
35 if (*chars_printed < n)
36 *(*s)++ = num_buf[i];
37 (*chars_printed)++;
38 }
39}
40
41/*******************************************************************
42 * Reduced snprintf to be used for Trusted firmware.
43 * The following type specifiers are supported:
44 *
45 * %d or %i - signed decimal format
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010046 * %s - string format
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010047 * %u - unsigned decimal format
48 *
49 * The function panics on all other formats specifiers.
50 *
51 * It returns the number of characters that would be written if the
52 * buffer was big enough. If it returns a value lower than n, the
53 * whole string has been written.
54 *******************************************************************/
55int tf_snprintf(char *s, size_t n, const char *fmt, ...)
56{
57 va_list args;
58 int num;
59 unsigned int unum;
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010060 char *str;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010061 size_t chars_printed = 0;
62
63 if (n == 1) {
64 /* Buffer is too small to actually write anything else. */
65 *s = '\0';
66 n = 0;
67 } else if (n >= 2) {
68 /* Reserve space for the terminator character. */
69 n--;
70 }
71
72 va_start(args, fmt);
73 while (*fmt) {
74
75 if (*fmt == '%') {
76 fmt++;
77 /* Check the format specifier. */
78 switch (*fmt) {
79 case 'i':
80 case 'd':
81 num = va_arg(args, int);
82
83 if (num < 0) {
84 if (chars_printed < n)
85 *s++ = '-';
86 chars_printed++;
87
88 unum = (unsigned int)-num;
89 } else {
90 unum = (unsigned int)num;
91 }
92
93 unsigned_dec_print(&s, n, &chars_printed, unum);
94 break;
Antonio Nino Diaz9fec10f2018-08-09 15:30:47 +010095 case 's':
96 str = va_arg(args, char *);
97 string_print(&s, n, &chars_printed, str);
98 break;
Antonio Nino Diaz9c107fa2017-05-17 15:34:22 +010099 case 'u':
100 unum = va_arg(args, unsigned int);
101 unsigned_dec_print(&s, n, &chars_printed, unum);
102 break;
103 default:
104 /* Panic on any other format specifier. */
105 ERROR("tf_snprintf: specifier with ASCII code '%d' not supported.",
106 *fmt);
107 plat_panic_handler();
108 }
109 fmt++;
110 continue;
111 }
112
113 if (chars_printed < n)
114 *s++ = *fmt;
115 fmt++;
116 chars_printed++;
117 }
118
119 va_end(args);
120
121 if (n > 0)
122 *s = '\0';
123
124 return chars_printed;
125}