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

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