PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/smbase/gprintf.c

http://github.com/dsw/oink-stack
C | 407 lines | 311 code | 49 blank | 47 comment | 86 complexity | 11c40d65cd2c54266a5f1a9e5b32d0d6 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-2.1
  1. /* gprintf.c */
  2. /* originally from: http://www.efgh.com/software/gprintf.htm */
  3. /* this file is in the public domain */
  4. /* modified by Scott McPeak, April 2003:
  5. * - use va_list instead of 'const int*' for the
  6. * pointer-to-argument type (for portability)
  7. * - implement conservative estimates for unknown format
  8. * chars, particularly 'f' (CONSERVATIVE_ESTIMATE flag)
  9. * - add a few test vectors
  10. */
  11. /* NOTE: There are quite a few differences among the *printf
  12. * implementations running around in the various libcs. The
  13. * implementation in this module doesn't know about all of those
  14. * variations and extensions. So, if you're using this to estimate
  15. * the # of chars your libc's printf will use, be sure to compare
  16. * libc's printf's actual return value, to make sure something doesn't
  17. * slip through the cracks. */
  18. /* Code for general_printf() */
  19. /* Change extension to .c before compiling */
  20. #include "gprintf.h" /* this module */
  21. #include <assert.h> /* assert */
  22. /* when this is true, unknown fields are filled with Xs, in an attempt
  23. * to print at least as many characters as libc's sprintf */
  24. #define CONSERVATIVE_ESTIMATE 1
  25. #define BITS_PER_BYTE 8
  26. struct parameters
  27. {
  28. int number_of_output_chars;
  29. short minimum_field_width;
  30. char options;
  31. #define MINUS_SIGN 1
  32. #define RIGHT_JUSTIFY 2
  33. #define ZERO_PAD 4
  34. #define CAPITAL_HEX 8
  35. short edited_string_length;
  36. short leading_zeros;
  37. int (*output_function)(void *, int);
  38. void *output_pointer;
  39. };
  40. static void output_and_count(struct parameters *p, int c)
  41. {
  42. if (p->number_of_output_chars >= 0)
  43. {
  44. int n = (*p->output_function)(p->output_pointer, c);
  45. if (n>=0) p->number_of_output_chars++;
  46. else p->number_of_output_chars = n;
  47. }
  48. }
  49. static void output_field(struct parameters *p, char const *s)
  50. {
  51. short justification_length =
  52. p->minimum_field_width - p->leading_zeros - p->edited_string_length;
  53. if (p->options & MINUS_SIGN)
  54. {
  55. if (p->options & ZERO_PAD)
  56. output_and_count(p, '-');
  57. justification_length--;
  58. }
  59. if (p->options & RIGHT_JUSTIFY)
  60. while (--justification_length >= 0)
  61. output_and_count(p, p->options & ZERO_PAD ? '0' : ' ');
  62. if (p->options & MINUS_SIGN && !(p->options & ZERO_PAD))
  63. output_and_count(p, '-');
  64. while (--p->leading_zeros >= 0)
  65. output_and_count(p, '0');
  66. while (--p->edited_string_length >= 0)
  67. output_and_count(p, *s++);
  68. while (--justification_length >= 0)
  69. output_and_count(p, ' ');
  70. }
  71. int general_vprintf(Gprintf_output_function output_function,
  72. void *output_pointer,
  73. const char *control_string,
  74. va_list argument_pointer)
  75. {
  76. struct parameters p;
  77. char control_char;
  78. p.number_of_output_chars = 0;
  79. p.output_function = output_function;
  80. p.output_pointer = output_pointer;
  81. control_char = *control_string++;
  82. while (control_char != '\0')
  83. {
  84. if (control_char == '%')
  85. {
  86. short precision = -1;
  87. short long_argument = 0;
  88. short base = 0;
  89. control_char = *control_string++;
  90. p.minimum_field_width = 0;
  91. p.leading_zeros = 0;
  92. p.options = RIGHT_JUSTIFY;
  93. if (control_char == '-')
  94. {
  95. p.options = 0;
  96. control_char = *control_string++;
  97. }
  98. if (control_char == '0')
  99. {
  100. p.options |= ZERO_PAD;
  101. control_char = *control_string++;
  102. }
  103. if (control_char == '*')
  104. {
  105. p.minimum_field_width = va_arg(argument_pointer, int);
  106. control_char = *control_string++;
  107. }
  108. else
  109. {
  110. while ('0' <= control_char && control_char <= '9')
  111. {
  112. p.minimum_field_width =
  113. p.minimum_field_width * 10 + control_char - '0';
  114. control_char = *control_string++;
  115. }
  116. }
  117. if (control_char == '.')
  118. {
  119. control_char = *control_string++;
  120. if (control_char == '*')
  121. {
  122. precision = va_arg(argument_pointer, int);
  123. control_char = *control_string++;
  124. }
  125. else
  126. {
  127. precision = 0;
  128. while ('0' <= control_char && control_char <= '9')
  129. {
  130. precision = precision * 10 + control_char - '0';
  131. control_char = *control_string++;
  132. }
  133. }
  134. }
  135. if (control_char == 'l')
  136. {
  137. long_argument = 1;
  138. control_char = *control_string++;
  139. }
  140. if (control_char == 'd')
  141. base = 10;
  142. else if (control_char == 'x')
  143. base = 16;
  144. else if (control_char == 'X')
  145. {
  146. base = 16;
  147. p.options |= CAPITAL_HEX;
  148. }
  149. else if (control_char == 'u')
  150. base = 10;
  151. else if (control_char == 'o')
  152. base = 8;
  153. else if (control_char == 'b')
  154. base = 2;
  155. else if (control_char == 'c')
  156. {
  157. base = -1;
  158. p.options &= ~ZERO_PAD;
  159. }
  160. else if (control_char == 's')
  161. {
  162. base = -2;
  163. p.options &= ~ZERO_PAD;
  164. }
  165. if (base == 0) /* invalid conversion type */
  166. {
  167. if (control_char != '\0')
  168. {
  169. #if !CONSERVATIVE_ESTIMATE
  170. /* sm: this was the original code; it just prints the
  171. * format character itself */
  172. output_and_count(&p, control_char);
  173. #else
  174. /* since my goal is actually to compute a conservative
  175. * upper bound on the # of chars output by sprintf, I want
  176. * to fill unknown fields with Xs */
  177. static char const * const XXX =
  178. "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"; /* 50 Xs */
  179. assert(precision <= 30); /* otherwise I need more Xs */
  180. /* I'm assuming that printing floating-point is the worst case.
  181. * I further assume non-fractional parts (integer part,
  182. * exponent, decimal, sign) won't exceed 20 chars. Finally,
  183. * up to 30 characters of decimal part are supported (this
  184. * is checked with the assertion above). */
  185. if (precision == -1) {
  186. p.edited_string_length = 20 + 6; /* 6 is default precision for 'f' */
  187. }
  188. else {
  189. p.edited_string_length = 20 + precision;
  190. }
  191. output_field(&p, XXX);
  192. #endif
  193. control_char = *control_string++;
  194. }
  195. }
  196. else
  197. {
  198. if (base == -1) /* conversion type c */
  199. {
  200. /* 'char' is passed as 'int' through '...' */
  201. char c = (char)va_arg(argument_pointer, int);
  202. p.edited_string_length = 1;
  203. output_field(&p, &c);
  204. }
  205. else if (base == -2) /* conversion type s */
  206. {
  207. char *string;
  208. p.edited_string_length = 0;
  209. string = va_arg(argument_pointer, char*);
  210. while (string[p.edited_string_length] != 0)
  211. p.edited_string_length++;
  212. if (precision >= 0 && p.edited_string_length > precision)
  213. p.edited_string_length = precision;
  214. output_field(&p, string);
  215. }
  216. else /* conversion type d, b, o or x */
  217. {
  218. unsigned long x;
  219. char buffer[BITS_PER_BYTE * sizeof(unsigned long) + 1];
  220. p.edited_string_length = 0;
  221. if (long_argument)
  222. {
  223. x = va_arg(argument_pointer, unsigned long);
  224. }
  225. else if (control_char == 'd')
  226. x = va_arg(argument_pointer, long);
  227. else
  228. x = va_arg(argument_pointer, unsigned);
  229. if (control_char == 'd' && (long) x < 0)
  230. {
  231. p.options |= MINUS_SIGN;
  232. x = - (long) x;
  233. }
  234. do
  235. {
  236. int c;
  237. c = x % base + '0';
  238. if (c > '9')
  239. {
  240. if (p.options & CAPITAL_HEX)
  241. c += 'A'-'9'-1;
  242. else
  243. c += 'a'-'9'-1;
  244. }
  245. buffer[sizeof(buffer) - 1 - p.edited_string_length++] = c;
  246. }
  247. while ((x/=base) != 0);
  248. if (precision >= 0 && precision > p.edited_string_length)
  249. p.leading_zeros = precision - p.edited_string_length;
  250. output_field(&p, buffer + sizeof(buffer) - p.edited_string_length);
  251. }
  252. control_char = *control_string++;
  253. }
  254. }
  255. else
  256. {
  257. output_and_count(&p, control_char);
  258. control_char = *control_string++;
  259. }
  260. }
  261. return p.number_of_output_chars;
  262. }
  263. int general_printf(Gprintf_output_function output,
  264. void *extra, const char *format, ...)
  265. {
  266. va_list args;
  267. int ret;
  268. va_start(args, format);
  269. ret = general_vprintf(output, extra, format, args);
  270. va_end(args);
  271. return ret;
  272. }
  273. /* ------------------ test code --------------------- */
  274. #ifdef TEST_GPRINTF
  275. #include <stdio.h> /* fputc, printf, vsprintf */
  276. #include <string.h> /* strcmp, strlen */
  277. #include <stdlib.h> /* exit */
  278. int string_output(void *extra, int ch)
  279. {
  280. /* the 'extra' argument is a pointer to a pointer to the
  281. * next character to write */
  282. char **s = (char**)extra;
  283. **s = ch; /* write */
  284. (*s)++; /* advance */
  285. return 0;
  286. }
  287. int general_vsprintf(char *dest, char const *format, va_list args)
  288. {
  289. char *s = dest;
  290. int ret;
  291. ret = general_vprintf(string_output, &s, format, args);
  292. *s = 0;
  293. return ret;
  294. }
  295. char output1[1024]; /* for libc */
  296. char output2[1024]; /* for this module */
  297. void expect_vector_len(int expect_len, char const *expect_output,
  298. char const *format, va_list args)
  299. {
  300. int len;
  301. static int vectors = 0;
  302. /* keep track of how many vectors we've tried, to make it
  303. * a little easier to correlate failures with the inputs
  304. * in this file */
  305. vectors++;
  306. /* run the generalized vsprintf */
  307. len = general_vsprintf(output2, format, args);
  308. /* compare */
  309. if (len!=expect_len ||
  310. 0!=strcmp(expect_output, output2)) {
  311. printf("outputs differ for vector %d!\n", vectors);
  312. printf(" format: %s\n", format);
  313. printf(" expect: %s (%d)\n", expect_output, expect_len);
  314. printf(" me: %s (%d)\n", output2, len);
  315. exit(2);
  316. }
  317. }
  318. void expect_vector(char const *expect_output,
  319. char const *format, ...)
  320. {
  321. va_list args;
  322. va_start(args, format);
  323. expect_vector_len(strlen(expect_output), expect_output, format, args);
  324. va_end(args);
  325. }
  326. void vector(char const *format, ...)
  327. {
  328. va_list args;
  329. int len;
  330. /* run the real vsprintf */
  331. va_start(args, format);
  332. len = vsprintf(output1, format, args);
  333. va_end(args);
  334. /* test against the generalized vsprintf */
  335. va_start(args, format);
  336. expect_vector_len(len, output1, format, args);
  337. va_end(args);
  338. }
  339. int main()
  340. {
  341. printf("testing gprintf...\n");
  342. /* test against libc */
  343. vector("simple");
  344. vector("a %s more", "little");
  345. vector("some %4d more %s complicated %c stuff",
  346. 33, "yikes", 'f');
  347. /* test unknown format chars */
  348. expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXX", "%f", 3.4);
  349. expect_vector("XXXXXXXXXXXXXXXXXXXXXXX", "%.3f", 3.4);
  350. expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "%.10f", 3.4);
  351. expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "%.30f", 3.4);
  352. /* fails assertion, as it should */
  353. /* expect_vector("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX", "%.31f", 3.4); */
  354. /* TODO: add more tests */
  355. printf("gprintf works\n");
  356. return 0;
  357. }
  358. #endif /* TEST_GPRINTF */