You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

vsprintf.c 9.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. */
  18. #include <stddef.h>
  19. #include <stdarg.h>
  20. #include <console.h>
  21. #include <errno.h>
  22. #include <vsprintf.h>
  23. /** @file */
  24. #define CHAR_LEN 0 /**< "hh" length modifier */
  25. #define SHORT_LEN 1 /**< "h" length modifier */
  26. #define INT_LEN 2 /**< no length modifier */
  27. #define LONG_LEN 3 /**< "l" length modifier */
  28. #define LONGLONG_LEN 4 /**< "ll" length modifier */
  29. #define SIZE_T_LEN 5 /**< "z" length modifier */
  30. static uint8_t type_sizes[] = {
  31. [CHAR_LEN] = sizeof ( char ),
  32. [SHORT_LEN] = sizeof ( short ),
  33. [INT_LEN] = sizeof ( int ),
  34. [LONG_LEN] = sizeof ( long ),
  35. [LONGLONG_LEN] = sizeof ( long long ),
  36. [SIZE_T_LEN] = sizeof ( size_t ),
  37. };
  38. /**
  39. * Use lower-case for hexadecimal digits
  40. *
  41. * Note that this value is set to 0x20 since that makes for very
  42. * efficient calculations. (Bitwise-ORing with @c LCASE converts to a
  43. * lower-case character, for example.)
  44. */
  45. #define LCASE 0x20
  46. /**
  47. * Use "alternate form"
  48. *
  49. * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
  50. * the number.
  51. */
  52. #define ALT_FORM 0x02
  53. /**
  54. * Format a hexadecimal number
  55. *
  56. * @v end End of buffer to contain number
  57. * @v num Number to format
  58. * @v width Minimum field width
  59. * @ret ptr End of buffer
  60. *
  61. * Fills a buffer in reverse order with a formatted hexadecimal
  62. * number. The number will be zero-padded to the specified width.
  63. * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
  64. * set.
  65. *
  66. * There must be enough space in the buffer to contain the largest
  67. * number that this function can format.
  68. */
  69. static char * format_hex ( char *end, unsigned long long num, int width,
  70. int flags ) {
  71. char *ptr = end;
  72. int case_mod;
  73. /* Generate the number */
  74. case_mod = flags & LCASE;
  75. do {
  76. *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
  77. num >>= 4;
  78. } while ( num );
  79. /* Zero-pad to width */
  80. while ( ( end - ptr ) < width )
  81. *(--ptr) = '0';
  82. /* Add "0x" or "0X" if alternate form specified */
  83. if ( flags & ALT_FORM ) {
  84. *(--ptr) = 'X' | case_mod;
  85. *(--ptr) = '0';
  86. }
  87. return ptr;
  88. }
  89. /**
  90. * Format a decimal number
  91. *
  92. * @v end End of buffer to contain number
  93. * @v num Number to format
  94. * @v width Minimum field width
  95. * @ret ptr End of buffer
  96. *
  97. * Fills a buffer in reverse order with a formatted decimal number.
  98. * The number will be space-padded to the specified width.
  99. *
  100. * There must be enough space in the buffer to contain the largest
  101. * number that this function can format.
  102. */
  103. static char * format_decimal ( char *end, signed long num, int width ) {
  104. char *ptr = end;
  105. int negative = 0;
  106. /* Generate the number */
  107. if ( num < 0 ) {
  108. negative = 1;
  109. num = -num;
  110. }
  111. do {
  112. *(--ptr) = '0' + ( num % 10 );
  113. num /= 10;
  114. } while ( num );
  115. /* Add "-" if necessary */
  116. if ( negative )
  117. *(--ptr) = '-';
  118. /* Space-pad to width */
  119. while ( ( end - ptr ) < width )
  120. *(--ptr) = ' ';
  121. return ptr;
  122. }
  123. /**
  124. * Print character via a printf context
  125. *
  126. * @v ctx Context
  127. * @v c Character
  128. *
  129. * Call's the printf_context::handler() method and increments
  130. * printf_context::len.
  131. */
  132. static inline void cputchar ( struct printf_context *ctx, unsigned int c ) {
  133. ctx->handler ( ctx, c );
  134. ++ctx->len;
  135. }
  136. /**
  137. * Write a formatted string to a printf context
  138. *
  139. * @v ctx Context
  140. * @v fmt Format string
  141. * @v args Arguments corresponding to the format string
  142. * @ret len Length of formatted string
  143. */
  144. size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
  145. int flags;
  146. int width;
  147. uint8_t *length;
  148. char *ptr;
  149. char tmp_buf[32]; /* 32 is enough for all numerical formats.
  150. * Insane width fields could overflow this buffer. */
  151. /* Initialise context */
  152. ctx->len = 0;
  153. for ( ; *fmt ; fmt++ ) {
  154. /* Pass through ordinary characters */
  155. if ( *fmt != '%' ) {
  156. cputchar ( ctx, *fmt );
  157. continue;
  158. }
  159. fmt++;
  160. /* Process flag characters */
  161. flags = 0;
  162. for ( ; ; fmt++ ) {
  163. if ( *fmt == '#' ) {
  164. flags |= ALT_FORM;
  165. } else if ( *fmt == '0' ) {
  166. /* We always 0-pad hex and space-pad decimal */
  167. } else {
  168. /* End of flag characters */
  169. break;
  170. }
  171. }
  172. /* Process field width */
  173. width = 0;
  174. for ( ; ; fmt++ ) {
  175. if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
  176. width = ( width * 10 ) + ( *fmt - '0' );
  177. } else {
  178. break;
  179. }
  180. }
  181. /* We don't do floating point */
  182. /* Process length modifier */
  183. length = &type_sizes[INT_LEN];
  184. for ( ; ; fmt++ ) {
  185. if ( *fmt == 'h' ) {
  186. length--;
  187. } else if ( *fmt == 'l' ) {
  188. length++;
  189. } else if ( *fmt == 'z' ) {
  190. length = &type_sizes[SIZE_T_LEN];
  191. } else {
  192. break;
  193. }
  194. }
  195. /* Process conversion specifier */
  196. ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
  197. *ptr = '\0';
  198. if ( *fmt == 'c' ) {
  199. cputchar ( ctx, va_arg ( args, unsigned int ) );
  200. } else if ( *fmt == 's' ) {
  201. ptr = va_arg ( args, char * );
  202. if ( ! ptr )
  203. ptr = "<NULL>";
  204. } else if ( *fmt == 'p' ) {
  205. intptr_t ptrval;
  206. ptrval = ( intptr_t ) va_arg ( args, void * );
  207. ptr = format_hex ( ptr, ptrval, width,
  208. ( ALT_FORM | LCASE ) );
  209. } else if ( ( *fmt & ~0x20 ) == 'X' ) {
  210. unsigned long long hex;
  211. flags |= ( *fmt & 0x20 ); /* LCASE */
  212. if ( *length >= sizeof ( unsigned long long ) ) {
  213. hex = va_arg ( args, unsigned long long );
  214. } else if ( *length >= sizeof ( unsigned long ) ) {
  215. hex = va_arg ( args, unsigned long );
  216. } else {
  217. hex = va_arg ( args, unsigned int );
  218. }
  219. ptr = format_hex ( ptr, hex, width, flags );
  220. } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
  221. signed long decimal;
  222. if ( *length >= sizeof ( signed long ) ) {
  223. decimal = va_arg ( args, signed long );
  224. } else {
  225. decimal = va_arg ( args, signed int );
  226. }
  227. ptr = format_decimal ( ptr, decimal, width );
  228. } else {
  229. *(--ptr) = *fmt;
  230. }
  231. /* Write out conversion result */
  232. for ( ; *ptr ; ptr++ ) {
  233. cputchar ( ctx, *ptr );
  234. }
  235. }
  236. return ctx->len;
  237. }
  238. /** Context used by vsnprintf() and friends */
  239. struct sputc_context {
  240. struct printf_context ctx;
  241. /** Buffer for formatted string (used by printf_sputc()) */
  242. char *buf;
  243. /** Buffer length (used by printf_sputc()) */
  244. size_t max_len;
  245. };
  246. /**
  247. * Write character to buffer
  248. *
  249. * @v ctx Context
  250. * @v c Character
  251. */
  252. static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
  253. struct sputc_context * sctx =
  254. container_of ( ctx, struct sputc_context, ctx );
  255. if ( ctx->len < sctx->max_len )
  256. sctx->buf[ctx->len] = c;
  257. }
  258. /**
  259. * Write a formatted string to a buffer
  260. *
  261. * @v buf Buffer into which to write the string
  262. * @v size Size of buffer
  263. * @v fmt Format string
  264. * @v args Arguments corresponding to the format string
  265. * @ret len Length of formatted string
  266. *
  267. * If the buffer is too small to contain the string, the returned
  268. * length is the length that would have been written had enough space
  269. * been available.
  270. */
  271. int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
  272. struct sputc_context sctx;
  273. size_t len;
  274. size_t end;
  275. /* Hand off to vcprintf */
  276. sctx.ctx.handler = printf_sputc;
  277. sctx.buf = buf;
  278. sctx.max_len = size;
  279. len = vcprintf ( &sctx.ctx, fmt, args );
  280. /* Add trailing NUL */
  281. if ( size ) {
  282. end = size - 1;
  283. if ( len < end )
  284. end = len;
  285. buf[end] = '\0';
  286. }
  287. return len;
  288. }
  289. /**
  290. * Write a formatted string to a buffer
  291. *
  292. * @v buf Buffer into which to write the string
  293. * @v size Size of buffer
  294. * @v fmt Format string
  295. * @v ... Arguments corresponding to the format string
  296. * @ret len Length of formatted string
  297. */
  298. int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
  299. va_list args;
  300. int i;
  301. va_start ( args, fmt );
  302. i = vsnprintf ( buf, size, fmt, args );
  303. va_end ( args );
  304. return i;
  305. }
  306. /**
  307. * Write character to console
  308. *
  309. * @v ctx Context
  310. * @v c Character
  311. */
  312. static void printf_putchar ( struct printf_context *ctx __unused,
  313. unsigned int c ) {
  314. putchar ( c );
  315. }
  316. /**
  317. * Write a formatted string to the console
  318. *
  319. * @v fmt Format string
  320. * @v args Arguments corresponding to the format string
  321. * @ret len Length of formatted string
  322. */
  323. int vprintf ( const char *fmt, va_list args ) {
  324. struct printf_context ctx;
  325. /* Hand off to vcprintf */
  326. ctx.handler = printf_putchar;
  327. return vcprintf ( &ctx, fmt, args );
  328. }
  329. /**
  330. * Write a formatted string to the console.
  331. *
  332. * @v fmt Format string
  333. * @v ... Arguments corresponding to the format string
  334. * @ret len Length of formatted string
  335. */
  336. int printf ( const char *fmt, ... ) {
  337. va_list args;
  338. int i;
  339. va_start ( args, fmt );
  340. i = vprintf ( fmt, args );
  341. va_end ( args );
  342. return i;
  343. }