snprintf.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. /*
  2. * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
  3. *
  4. * SPDX-License-Identifier: BSD-3-Clause
  5. */
  6. #include <assert.h>
  7. #include <stdarg.h>
  8. #include <stdbool.h>
  9. #include <stddef.h>
  10. #include <stdint.h>
  11. #define get_num_va_args(_args, _lcount) \
  12. (((_lcount) > 1) ? va_arg(_args, long long int) : \
  13. (((_lcount) == 1) ? va_arg(_args, long int) : \
  14. va_arg(_args, int)))
  15. #define get_unum_va_args(_args, _lcount) \
  16. (((_lcount) > 1) ? va_arg(_args, unsigned long long int) : \
  17. (((_lcount) == 1) ? va_arg(_args, unsigned long int) : \
  18. va_arg(_args, unsigned int)))
  19. #define CHECK_AND_PUT_CHAR(buf, size, chars_printed, ch) \
  20. do { \
  21. if ((chars_printed) < (size)) { \
  22. *(buf) = (ch); \
  23. (buf)++; \
  24. } \
  25. (chars_printed)++; \
  26. } while (false)
  27. static void string_print(char **s, size_t n, size_t *chars_printed,
  28. const char *str)
  29. {
  30. while (*str != '\0') {
  31. CHECK_AND_PUT_CHAR(*s, n, *chars_printed, *str);
  32. str++;
  33. }
  34. }
  35. static void unsigned_num_print(char **s, size_t n, size_t *chars_printed,
  36. unsigned long long int unum,
  37. unsigned int radix, char padc, int padn,
  38. bool capitalise)
  39. {
  40. /* Just need enough space to store 64 bit decimal integer */
  41. char num_buf[20];
  42. int i = 0;
  43. int width;
  44. unsigned int rem;
  45. char ascii_a = capitalise ? 'A' : 'a';
  46. /* num_buf is only large enough for radix >= 10 */
  47. if (radix < 10) {
  48. assert(0);
  49. return;
  50. }
  51. do {
  52. rem = unum % radix;
  53. if (rem < 10U) {
  54. num_buf[i] = '0' + rem;
  55. } else {
  56. num_buf[i] = ascii_a + (rem - 10U);
  57. }
  58. i++;
  59. unum /= radix;
  60. } while (unum > 0U);
  61. width = i;
  62. for (i = padn - width; i > 0; i--) {
  63. CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
  64. }
  65. for (i = width; i > 0; i--) {
  66. CHECK_AND_PUT_CHAR(*s, n, *chars_printed, num_buf[i - 1]);
  67. }
  68. for (i = width + padn; i < 0; i++) {
  69. CHECK_AND_PUT_CHAR(*s, n, *chars_printed, padc);
  70. }
  71. }
  72. /*******************************************************************
  73. * Reduced vsnprintf to be used for Trusted firmware.
  74. * The following type specifiers are supported:
  75. *
  76. * %x (or %X) - hexadecimal format
  77. * %d or %i - signed decimal format
  78. * %c - character format
  79. * %s - string format
  80. * %u - unsigned decimal format
  81. * %p - pointer format
  82. *
  83. * The following length specifiers are supported by this print
  84. * %l - long int
  85. * %ll - long long int
  86. * %z - size_t sized integer formats
  87. *
  88. * The following padding specifiers are supported by this print
  89. * %0NN - Left-pad the number with 0s (NN is a decimal number)
  90. * %NN - Left-pad the number or string with spaces (NN is a decimal number)
  91. * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
  92. *
  93. * The function panics on all other formats specifiers.
  94. *
  95. * It returns the number of characters that would be written if the
  96. * buffer was big enough. If it returns a value lower than n, the
  97. * whole string has been written.
  98. *******************************************************************/
  99. int vsnprintf(char *s, size_t n, const char *fmt, va_list args)
  100. {
  101. int num;
  102. unsigned long long int unum;
  103. char *str;
  104. char padc; /* Padding character */
  105. int padn; /* Number of characters to pad */
  106. bool left;
  107. bool capitalise;
  108. size_t chars_printed = 0U;
  109. unsigned int l_count;
  110. if (n == 0U) {
  111. /* There isn't space for anything. */
  112. } else if (n == 1U) {
  113. /* Buffer is too small to actually write anything else. */
  114. *s = '\0';
  115. n = 0U;
  116. } else {
  117. /* Reserve space for the terminator character. */
  118. n--;
  119. }
  120. while (*fmt != '\0') {
  121. left = false;
  122. padc ='\0';
  123. padn = 0;
  124. capitalise = false;
  125. l_count = 0;
  126. if (*fmt == '%') {
  127. fmt++;
  128. /* Check the format specifier. */
  129. loop:
  130. switch (*fmt) {
  131. case '%':
  132. CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
  133. break;
  134. case '0':
  135. case '1':
  136. case '2':
  137. case '3':
  138. case '4':
  139. case '5':
  140. case '6':
  141. case '7':
  142. case '8':
  143. case '9':
  144. padc = (*fmt == '0') ? '0' : ' ';
  145. for (padn = 0; *fmt >= '0' && *fmt <= '9'; fmt++) {
  146. padn = (padn * 10) + (*fmt - '0');
  147. }
  148. if (left) {
  149. padn = -padn;
  150. }
  151. goto loop;
  152. case '-':
  153. left = true;
  154. fmt++;
  155. goto loop;
  156. case 'i':
  157. case 'd':
  158. num = get_num_va_args(args, l_count);
  159. if (num < 0) {
  160. CHECK_AND_PUT_CHAR(s, n, chars_printed,
  161. '-');
  162. unum = (unsigned int)-num;
  163. } else {
  164. unum = (unsigned int)num;
  165. }
  166. unsigned_num_print(&s, n, &chars_printed,
  167. unum, 10, padc, padn, false);
  168. break;
  169. case 'c':
  170. CHECK_AND_PUT_CHAR(s, n, chars_printed, va_arg(args, int));
  171. break;
  172. case 's':
  173. str = va_arg(args, char *);
  174. string_print(&s, n, &chars_printed, str);
  175. break;
  176. case 'u':
  177. unum = get_unum_va_args(args, l_count);
  178. unsigned_num_print(&s, n, &chars_printed,
  179. unum, 10, padc, padn, false);
  180. break;
  181. case 'z':
  182. l_count = 1;
  183. fmt++;
  184. goto loop;
  185. case 'l':
  186. l_count++;
  187. fmt++;
  188. goto loop;
  189. case 'p':
  190. unum = (uintptr_t)va_arg(args, void *);
  191. if (unum > 0U) {
  192. string_print(&s, n, &chars_printed, "0x");
  193. padn -= 2;
  194. }
  195. unsigned_num_print(&s, n, &chars_printed,
  196. unum, 16, padc, padn, false);
  197. break;
  198. case 'X':
  199. capitalise = true;
  200. /* fallthrough */
  201. case 'x':
  202. unum = get_unum_va_args(args, l_count);
  203. unsigned_num_print(&s, n, &chars_printed,
  204. unum, 16, padc, padn,
  205. capitalise);
  206. break;
  207. default:
  208. CHECK_AND_PUT_CHAR(s, n, chars_printed, '%');
  209. CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
  210. }
  211. fmt++;
  212. continue;
  213. }
  214. CHECK_AND_PUT_CHAR(s, n, chars_printed, *fmt);
  215. fmt++;
  216. }
  217. if (n > 0U) {
  218. *s = '\0';
  219. }
  220. return (int)chars_printed;
  221. }
  222. /*******************************************************************
  223. * Reduced snprintf to be used for Trusted firmware.
  224. * The following type specifiers are supported:
  225. *
  226. * %x (or %X) - hexadecimal format
  227. * %d or %i - signed decimal format
  228. * %s - string format
  229. * %u - unsigned decimal format
  230. * %p - pointer format
  231. *
  232. * The following padding specifiers are supported by this print
  233. * %0NN - Left-pad the number with 0s (NN is a decimal number)
  234. * %NN - Left-pad the number or string with spaces (NN is a decimal number)
  235. * %-NN - Right-pad the number or string with spaces (NN is a decimal number)
  236. *
  237. * The function panics on all other formats specifiers.
  238. *
  239. * It returns the number of characters that would be written if the
  240. * buffer was big enough. If it returns a value lower than n, the
  241. * whole string has been written.
  242. *******************************************************************/
  243. int snprintf(char *s, size_t n, const char *fmt, ...)
  244. {
  245. int count;
  246. va_list all_args;
  247. va_start(all_args, fmt);
  248. count = vsnprintf(s, n, fmt, all_args);
  249. va_end(all_args);
  250. return count;
  251. }