do_printf.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. #include <string.h> /* strlen() */
  2. #include "do_printf.h"
  3. /*****************************************************************************
  4. Revised Jan 28, 2002
  5. - changes to make characters 0x80-0xFF display properly
  6. Revised June 10, 2001
  7. - changes to make vsprintf() terminate string with '\0'
  8. Revised May 12, 2000
  9. - math in DO_NUM is now unsigned, as it should be
  10. - %0 flag (pad left with zeroes) now works
  11. - actually did some TESTING, maybe fixed some other bugs
  12. name: do_printf
  13. action: minimal subfunction for ?printf, calls function
  14. 'fn' with arg 'ptr' for each character to be output
  15. returns:total number of characters output
  16. %[flag][width][.prec][mod][conv]
  17. flag: - left justify, pad right w/ blanks DONE
  18. 0 pad left w/ 0 for numerics DONE
  19. + always print sign, + or - no
  20. ' ' (blank) no
  21. # (???) no
  22. width: (field width) DONE
  23. prec: (precision) no
  24. conv: d,i decimal int DONE
  25. u decimal unsigned DONE
  26. o octal DONE
  27. x,X hex DONE
  28. f,e,g,E,G float no
  29. c char DONE
  30. s string DONE
  31. p ptr DONE
  32. mod: N near ptr DONE
  33. F far ptr no
  34. h short (16-bit) int DONE
  35. l long (32-bit) int DONE
  36. L long long (64-bit) int no
  37. *****************************************************************************/
  38. /* flags used in processing format string */
  39. #define PR_LJ 0x01 /* left justify */
  40. #define PR_CA 0x02 /* use A-F instead of a-f for hex */
  41. #define PR_SG 0x04 /* signed numeric conversion (%d vs. %u) */
  42. #define PR_32 0x08 /* long (32-bit) numeric conversion */
  43. #define PR_16 0x10 /* short (16-bit) numeric conversion */
  44. #define PR_WS 0x20 /* PR_SG set and num was < 0 */
  45. #define PR_LZ 0x40 /* pad left with '0' instead of ' ' */
  46. #define PR_FP 0x80 /* pointers are far */
  47. /* largest number handled is 2^32-1, lowest radix handled is 8.
  48. 2^32-1 in base 8 has 11 digits (add 5 for trailing NUL and for slop) */
  49. #define PR_BUFLEN 16
  50. int do_printf(const char *fmt, va_list args, fnptr_t fn, void *ptr)
  51. {
  52. unsigned state, flags, radix, actual_wd, count, given_wd;
  53. unsigned char *where, buf[PR_BUFLEN];
  54. long num;
  55. state = flags = count = given_wd = 0;
  56. /* begin scanning format specifier list */
  57. for(; *fmt; fmt++)
  58. {
  59. switch(state)
  60. {
  61. /* STATE 0: AWAITING % */
  62. case 0:
  63. if(*fmt != '%') /* not %... */
  64. {
  65. fn(*fmt, &ptr); /* ...just echo it */
  66. count++;
  67. break;
  68. }
  69. /* found %, get next char and advance state to check if next char is a flag */
  70. state++;
  71. fmt++;
  72. /* FALL THROUGH */
  73. /* STATE 1: AWAITING FLAGS (%-0) */
  74. case 1:
  75. if(*fmt == '%') /* %% */
  76. {
  77. fn(*fmt, &ptr);
  78. count++;
  79. state = flags = given_wd = 0;
  80. break;
  81. }
  82. if(*fmt == '-')
  83. {
  84. if(flags & PR_LJ)/* %-- is illegal */
  85. state = flags = given_wd = 0;
  86. else
  87. flags |= PR_LJ;
  88. break;
  89. }
  90. /* not a flag char: advance state to check if it's field width */
  91. state++;
  92. /* check now for '%0...' */
  93. if(*fmt == '0')
  94. {
  95. flags |= PR_LZ;
  96. fmt++;
  97. }
  98. /* FALL THROUGH */
  99. /* STATE 2: AWAITING (NUMERIC) FIELD WIDTH */
  100. case 2:
  101. if(*fmt >= '0' && *fmt <= '9')
  102. {
  103. given_wd = 10 * given_wd +
  104. (*fmt - '0');
  105. break;
  106. }
  107. /* not field width: advance state to check if it's a modifier */
  108. state++;
  109. /* FALL THROUGH */
  110. /* STATE 3: AWAITING MODIFIER CHARS (FNlh) */
  111. case 3:
  112. if(*fmt == 'F')
  113. {
  114. flags |= PR_FP;
  115. break;
  116. }
  117. if(*fmt == 'N')
  118. break;
  119. if(*fmt == 'l')
  120. {
  121. flags |= PR_32;
  122. break;
  123. }
  124. if(*fmt == 'h')
  125. {
  126. flags |= PR_16;
  127. break;
  128. }
  129. /* not modifier: advance state to check if it's a conversion char */
  130. state++;
  131. /* FALL THROUGH */
  132. /* STATE 4: AWAITING CONVERSION CHARS (Xxpndiuocs) */
  133. case 4:
  134. where = buf + PR_BUFLEN - 1;
  135. *where = '\0';
  136. switch(*fmt)
  137. {
  138. case 'X':
  139. flags |= PR_CA;
  140. /* FALL THROUGH */
  141. /* xxx - far pointers (%Fp, %Fn) not yet supported */
  142. case 'x':
  143. case 'p':
  144. case 'n':
  145. radix = 16;
  146. goto DO_NUM;
  147. case 'd':
  148. case 'i':
  149. flags |= PR_SG;
  150. /* FALL THROUGH */
  151. case 'u':
  152. radix = 10;
  153. goto DO_NUM;
  154. case 'o':
  155. radix = 8;
  156. /* load the value to be printed. l=long=32 bits: */
  157. DO_NUM: if(flags & PR_32)
  158. num = va_arg(args, unsigned long);
  159. /* h=short=16 bits (signed or unsigned) */
  160. else if(flags & PR_16)
  161. {
  162. if(flags & PR_SG)
  163. num = va_arg(args, unsigned long);
  164. else
  165. num = va_arg(args, unsigned long);
  166. }
  167. /* no h nor l: sizeof(int) bits (signed or unsigned) */
  168. else
  169. {
  170. if(flags & PR_SG)
  171. num = va_arg(args, unsigned long);
  172. else
  173. num = va_arg(args, unsigned long);
  174. }
  175. /* take care of sign */
  176. if(flags & PR_SG)
  177. {
  178. if(num < 0)
  179. {
  180. flags |= PR_WS;
  181. num = -num;
  182. }
  183. }
  184. /* convert binary to octal/decimal/hex ASCII
  185. OK, I found my mistake. The math here is _always_ unsigned */
  186. do
  187. {
  188. unsigned long temp;
  189. temp = (unsigned long)num % radix;
  190. where--;
  191. if(temp < 10)
  192. *where = temp + '0';
  193. else if(flags & PR_CA)
  194. *where = temp - 10 + 'A';
  195. else
  196. *where = temp - 10 + 'a';
  197. num = (unsigned long)num / radix;
  198. }
  199. while(num != 0);
  200. goto EMIT;
  201. case 'c':
  202. /* disallow pad-left-with-zeroes for %c */
  203. flags &= ~PR_LZ;
  204. where--;
  205. *where = (unsigned char)va_arg(args,
  206. unsigned int);
  207. actual_wd = 1;
  208. goto EMIT2;
  209. case 's':
  210. /* disallow pad-left-with-zeroes for %s */
  211. flags &= ~PR_LZ;
  212. where = (unsigned char *)va_arg(args, unsigned int);
  213. EMIT:
  214. actual_wd = strlen((const char *)where);
  215. if(flags & PR_WS)
  216. actual_wd++;
  217. /* if we pad left with ZEROES, do the sign now */
  218. if((flags & (PR_WS | PR_LZ)) ==
  219. (PR_WS | PR_LZ))
  220. {
  221. fn('-', &ptr);
  222. count++;
  223. }
  224. /* pad on left with spaces or zeroes (for right justify) */
  225. EMIT2: if((flags & PR_LJ) == 0)
  226. {
  227. while(given_wd > actual_wd)
  228. {
  229. fn(flags & PR_LZ ? '0' :
  230. ' ', &ptr);
  231. count++;
  232. given_wd--;
  233. }
  234. }
  235. /* if we pad left with SPACES, do the sign now */
  236. if((flags & (PR_WS | PR_LZ)) == PR_WS)
  237. {
  238. fn('-', &ptr);
  239. count++;
  240. }
  241. /* emit string/char/converted number */
  242. while(*where != '\0')
  243. {
  244. fn(*where++, &ptr);
  245. count++;
  246. }
  247. /* pad on right with spaces (for left justify) */
  248. if(given_wd < actual_wd)
  249. given_wd = 0;
  250. else given_wd -= actual_wd;
  251. for(; given_wd; given_wd--)
  252. {
  253. fn(' ', &ptr);
  254. count++;
  255. }
  256. break;
  257. default:
  258. break;
  259. }
  260. default:
  261. state = flags = given_wd = 0;
  262. break;
  263. }
  264. }
  265. return count;
  266. }