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 10KB

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