printfmt.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /* Implementation of printf console output for user environments.
  2. *
  3. * printf is a debugging statement, not a generic output statement.
  4. * It is very important that it always go to the console, especially when
  5. * debugging file descriptor code! */
  6. #include <stdint.h>
  7. #include <stdarg.h>
  8. #include "api.h"
  9. // Print a number (base <= 16) in reverse order,
  10. // using specified fputch function and associated pointer putdat.
  11. #if !defined(__i386__)
  12. static void
  13. printnum(void (*_fputch)(void *, int, void *), void * f, void * putdat,
  14. unsigned long long num, unsigned base, int width, int padc)
  15. #else
  16. static void
  17. printnum(void (*_fputch)(void *, int, void *), void * f, void * putdat,
  18. unsigned long num, unsigned base, int width, int padc)
  19. #endif
  20. {
  21. // first recursively print all preceding (more significant) digits
  22. if (num >= base) {
  23. printnum(_fputch, f, putdat, num / base, base, width - 1, padc);
  24. } else {
  25. // print any needed pad characters before first digit
  26. while (--width > 0)
  27. (*_fputch) (f, padc, putdat);
  28. }
  29. // then print this (the least significant) digit
  30. (*_fputch) (f, "0123456789abcdef"[num % base], putdat);
  31. }
  32. // Get an unsigned int of various possible sizes from a varargs list,
  33. // depending on the lflag parameter.
  34. #if !defined(__i386__)
  35. inline unsigned long long
  36. getuint(va_list *ap, int lflag)
  37. #else
  38. inline unsigned long
  39. getuint(va_list *ap, int lflag)
  40. #endif
  41. {
  42. #if !defined(__i386__)
  43. if (lflag >= 2)
  44. return va_arg(*ap, unsigned long long);
  45. else
  46. #endif
  47. if (lflag)
  48. return va_arg(*ap, unsigned long);
  49. else
  50. return va_arg(*ap, unsigned int);
  51. }
  52. // Same as getuint but signed - can't use getuint
  53. // because of sign extension
  54. #if !defined(__i386__)
  55. inline long long
  56. getint(va_list *ap, int lflag)
  57. #else
  58. inline long
  59. getint(va_list *ap, int lflag)
  60. #endif
  61. {
  62. #if !defined(__i386__)
  63. if (lflag >= 2)
  64. return va_arg(*ap, long long);
  65. else
  66. #endif
  67. if (lflag)
  68. return va_arg(*ap, long);
  69. else
  70. return va_arg(*ap, int);
  71. }
  72. // Main function to format and print a string.
  73. void fprintfmt(void (*_fputch)(void *, int, void *), void * f, void * putdat,
  74. const char * fmt, ...);
  75. void
  76. vfprintfmt(void (*_fputch)(void *, int, void *), void * f, void * putdat,
  77. const char * fmt, va_list *ap)
  78. {
  79. register const char *p;
  80. register int ch;
  81. #if !defined(__i386__)
  82. unsigned long long num;
  83. #else
  84. unsigned long num;
  85. #endif
  86. int base, lflag, width, precision, altflag;
  87. char padc;
  88. while (1) {
  89. while ((ch = *(unsigned char *) (fmt++)) != '%') {
  90. if (ch == '\0')
  91. return;
  92. (*_fputch) (f, ch, putdat);
  93. }
  94. // Process a %-escape sequence
  95. padc = ' ';
  96. width = -1;
  97. precision = -1;
  98. lflag = 0;
  99. altflag = 0;
  100. reswitch:
  101. switch (ch = *(unsigned char *) (fmt++)) {
  102. // flag to pad on the right
  103. case '-':
  104. padc = ' ';
  105. goto reswitch;
  106. // flag to pad with 0's instead of spaces
  107. case '0':
  108. padc = '0';
  109. goto reswitch;
  110. // width field
  111. case '1':
  112. case '2':
  113. case '3':
  114. case '4':
  115. case '5':
  116. case '6':
  117. case '7':
  118. case '8':
  119. case '9':
  120. for (precision = 0; ; ++fmt) {
  121. precision = precision * 10 + ch - '0';
  122. ch = *fmt;
  123. if (ch < '0' || ch > '9')
  124. break;
  125. }
  126. goto process_precision;
  127. case '*':
  128. precision = va_arg(*ap, int);
  129. goto process_precision;
  130. case '.':
  131. if (width < 0)
  132. width = 0;
  133. goto reswitch;
  134. case '#':
  135. altflag = 1;
  136. goto reswitch;
  137. process_precision:
  138. if (width < 0)
  139. width = precision, precision = -1;
  140. goto reswitch;
  141. // long flag (doubled for long long)
  142. case 'l':
  143. lflag++;
  144. goto reswitch;
  145. // character
  146. case 'c':
  147. (*_fputch) (f, va_arg(*ap, int), putdat);
  148. break;
  149. // string
  150. case 's':
  151. if ((p = va_arg(*ap, char *)) == NULL)
  152. p = "(null)";
  153. if (width > 0 && padc != '-')
  154. for (width -= strnlen(p, precision); width > 0; width--)
  155. (*_fputch) (f, padc, putdat);
  156. for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--)
  157. if (altflag && (ch < ' ' || ch > '~'))
  158. (*_fputch) (f, '?', putdat);
  159. else
  160. (*_fputch) (f, ch, putdat);
  161. for (; width > 0; width--)
  162. (*_fputch) (f, ' ', putdat);
  163. break;
  164. // (signed) decimal
  165. case 'd':
  166. num = getint(ap, lflag);
  167. #if !defined(__i386__)
  168. if ((long long) num < 0) {
  169. (*_fputch) (f, '-', putdat);
  170. num = -(long long) num;
  171. }
  172. #else
  173. if ((long) num < 0) {
  174. (*_fputch) (f, '-', putdat);
  175. num = -(long) num;
  176. }
  177. #endif
  178. base = 10;
  179. goto number;
  180. // unsigned decimal
  181. case 'u':
  182. num = getuint(ap, lflag);
  183. base = 10;
  184. goto number;
  185. // (unsigned) octal
  186. case 'o':
  187. // Replace this with your code.
  188. num = getuint(ap, lflag);
  189. base = 8;
  190. goto number;
  191. // pointer
  192. case 'p':
  193. (*_fputch) (f, '0', putdat);
  194. (*_fputch) (f, 'x', putdat);
  195. #if !defined(__i386__)
  196. num = (unsigned long long)
  197. (uintptr_t) va_arg(*ap, void *);
  198. #else
  199. num = (unsigned long)
  200. (uintptr_t) va_arg(*ap, void *);
  201. #endif
  202. base = 16;
  203. goto number;
  204. // (unsigned) hexadecimal
  205. case 'x':
  206. num = getuint(ap, lflag);
  207. base = 16;
  208. number:
  209. printnum(_fputch, f, putdat, num, base, width, padc);
  210. break;
  211. // escape character
  212. case '^':
  213. (*_fputch) (f, 0x1b, putdat);
  214. break;
  215. // escaped '%' character
  216. case '%':
  217. (*_fputch) (f, ch, putdat);
  218. break;
  219. // unrecognized escape sequence - just print it literally
  220. default:
  221. (*_fputch) (f, '%', putdat);
  222. for (fmt--; fmt[-1] != '%'; fmt--)
  223. /* do nothing */;
  224. break;
  225. }
  226. }
  227. }
  228. void
  229. fprintfmt(void (*_fputch)(void *, int, void *), void * f, void * putdat,
  230. const char * fmt, ...)
  231. {
  232. va_list ap;
  233. va_start(ap, fmt);
  234. vfprintfmt(_fputch, f, putdat, fmt, &ap);
  235. va_end(ap);
  236. }
  237. struct sprintbuf {
  238. char * buf;
  239. char * ebuf;
  240. int cnt;
  241. };
  242. static void
  243. sprintputch(void * f, int ch, struct sprintbuf * b)
  244. {
  245. b->cnt++;
  246. if (b->buf < b->ebuf)
  247. *b->buf++ = ch;
  248. }
  249. static int
  250. vsprintf(char * buf, int n, const char * fmt, va_list *ap)
  251. {
  252. struct sprintbuf b = {buf, buf + n - 1, 0};
  253. if (buf == NULL || n < 1) {
  254. return -1;
  255. }
  256. // print the string to the buffer
  257. vfprintfmt((void *) sprintputch, (void *) 0, &b, fmt, ap);
  258. // null terminate the buffer
  259. *b.buf = '\0';
  260. return b.cnt;
  261. }
  262. int
  263. snprintf(char * buf, int n, const char * fmt, ...)
  264. {
  265. va_list ap;
  266. int rc;
  267. va_start(ap, fmt);
  268. rc = vsprintf(buf, n, fmt, &ap);
  269. va_end(ap);
  270. return rc;
  271. }