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

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