《操作系统》的实验代码。
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.

340 lines
9.5 KiB

  1. #include <defs.h>
  2. #include <x86.h>
  3. #include <error.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. /* *
  7. * Space or zero padding and a field width are supported for the numeric
  8. * formats only.
  9. *
  10. * The special format %e takes an integer error code
  11. * and prints a string describing the error.
  12. * The integer may be positive or negative,
  13. * so that -E_NO_MEM and E_NO_MEM are equivalent.
  14. * */
  15. static const char * const error_string[MAXERROR + 1] = {
  16. [0] NULL,
  17. [E_UNSPECIFIED] "unspecified error",
  18. [E_BAD_PROC] "bad process",
  19. [E_INVAL] "invalid parameter",
  20. [E_NO_MEM] "out of memory",
  21. [E_NO_FREE_PROC] "out of processes",
  22. [E_FAULT] "segmentation fault",
  23. };
  24. /* *
  25. * printnum - print a number (base <= 16) in reverse order
  26. * @putch: specified putch function, print a single character
  27. * @putdat: used by @putch function
  28. * @num: the number will be printed
  29. * @base: base for print, must be in [1, 16]
  30. * @width: maximum number of digits, if the actual width is less than @width, use @padc instead
  31. * @padc: character that padded on the left if the actual width is less than @width
  32. * */
  33. static void
  34. printnum(void (*putch)(int, void*), void *putdat,
  35. unsigned long long num, unsigned base, int width, int padc) {
  36. unsigned long long result = num;
  37. unsigned mod = do_div(result, base);
  38. // first recursively print all preceding (more significant) digits
  39. if (num >= base) {
  40. printnum(putch, putdat, result, base, width - 1, padc);
  41. } else {
  42. // print any needed pad characters before first digit
  43. while (-- width > 0)
  44. putch(padc, putdat);
  45. }
  46. // then print this (the least significant) digit
  47. putch("0123456789abcdef"[mod], putdat);
  48. }
  49. /* *
  50. * getuint - get an unsigned int of various possible sizes from a varargs list
  51. * @ap: a varargs list pointer
  52. * @lflag: determines the size of the vararg that @ap points to
  53. * */
  54. static unsigned long long
  55. getuint(va_list *ap, int lflag) {
  56. if (lflag >= 2) {
  57. return va_arg(*ap, unsigned long long);
  58. }
  59. else if (lflag) {
  60. return va_arg(*ap, unsigned long);
  61. }
  62. else {
  63. return va_arg(*ap, unsigned int);
  64. }
  65. }
  66. /* *
  67. * getint - same as getuint but signed, we can't use getuint because of sign extension
  68. * @ap: a varargs list pointer
  69. * @lflag: determines the size of the vararg that @ap points to
  70. * */
  71. static long long
  72. getint(va_list *ap, int lflag) {
  73. if (lflag >= 2) {
  74. return va_arg(*ap, long long);
  75. }
  76. else if (lflag) {
  77. return va_arg(*ap, long);
  78. }
  79. else {
  80. return va_arg(*ap, int);
  81. }
  82. }
  83. /* *
  84. * printfmt - format a string and print it by using putch
  85. * @putch: specified putch function, print a single character
  86. * @putdat: used by @putch function
  87. * @fmt: the format string to use
  88. * */
  89. void
  90. printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) {
  91. va_list ap;
  92. va_start(ap, fmt);
  93. vprintfmt(putch, putdat, fmt, ap);
  94. va_end(ap);
  95. }
  96. /* *
  97. * vprintfmt - format a string and print it by using putch, it's called with a va_list
  98. * instead of a variable number of arguments
  99. * @putch: specified putch function, print a single character
  100. * @putdat: used by @putch function
  101. * @fmt: the format string to use
  102. * @ap: arguments for the format string
  103. *
  104. * Call this function if you are already dealing with a va_list.
  105. * Or you probably want printfmt() instead.
  106. * */
  107. void
  108. vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) {
  109. register const char *p;
  110. register int ch, err;
  111. unsigned long long num;
  112. int base, width, precision, lflag, altflag;
  113. while (1) {
  114. while ((ch = *(unsigned char *)fmt ++) != '%') {
  115. if (ch == '\0') {
  116. return;
  117. }
  118. putch(ch, putdat);
  119. }
  120. // Process a %-escape sequence
  121. char padc = ' ';
  122. width = precision = -1;
  123. lflag = altflag = 0;
  124. reswitch:
  125. switch (ch = *(unsigned char *)fmt ++) {
  126. // flag to pad on the right
  127. case '-':
  128. padc = '-';
  129. goto reswitch;
  130. // flag to pad with 0's instead of spaces
  131. case '0':
  132. padc = '0';
  133. goto reswitch;
  134. // width field
  135. case '1' ... '9':
  136. for (precision = 0; ; ++ fmt) {
  137. precision = precision * 10 + ch - '0';
  138. ch = *fmt;
  139. if (ch < '0' || ch > '9') {
  140. break;
  141. }
  142. }
  143. goto process_precision;
  144. case '*':
  145. precision = va_arg(ap, int);
  146. goto process_precision;
  147. case '.':
  148. if (width < 0)
  149. width = 0;
  150. goto reswitch;
  151. case '#':
  152. altflag = 1;
  153. goto reswitch;
  154. process_precision:
  155. if (width < 0)
  156. width = precision, precision = -1;
  157. goto reswitch;
  158. // long flag (doubled for long long)
  159. case 'l':
  160. lflag ++;
  161. goto reswitch;
  162. // character
  163. case 'c':
  164. putch(va_arg(ap, int), putdat);
  165. break;
  166. // error message
  167. case 'e':
  168. err = va_arg(ap, int);
  169. if (err < 0) {
  170. err = -err;
  171. }
  172. if (err > MAXERROR || (p = error_string[err]) == NULL) {
  173. printfmt(putch, putdat, "error %d", err);
  174. }
  175. else {
  176. printfmt(putch, putdat, "%s", p);
  177. }
  178. break;
  179. // string
  180. case 's':
  181. if ((p = va_arg(ap, char *)) == NULL) {
  182. p = "(null)";
  183. }
  184. if (width > 0 && padc != '-') {
  185. for (width -= strnlen(p, precision); width > 0; width --) {
  186. putch(padc, putdat);
  187. }
  188. }
  189. for (; (ch = *p ++) != '\0' && (precision < 0 || -- precision >= 0); width --) {
  190. if (altflag && (ch < ' ' || ch > '~')) {
  191. putch('?', putdat);
  192. }
  193. else {
  194. putch(ch, putdat);
  195. }
  196. }
  197. for (; width > 0; width --) {
  198. putch(' ', putdat);
  199. }
  200. break;
  201. // (signed) decimal
  202. case 'd':
  203. num = getint(&ap, lflag);
  204. if ((long long)num < 0) {
  205. putch('-', putdat);
  206. num = -(long long)num;
  207. }
  208. base = 10;
  209. goto number;
  210. // unsigned decimal
  211. case 'u':
  212. num = getuint(&ap, lflag);
  213. base = 10;
  214. goto number;
  215. // (unsigned) octal
  216. case 'o':
  217. num = getuint(&ap, lflag);
  218. base = 8;
  219. goto number;
  220. // pointer
  221. case 'p':
  222. putch('0', putdat);
  223. putch('x', putdat);
  224. num = (unsigned long long)(uintptr_t)va_arg(ap, void *);
  225. base = 16;
  226. goto number;
  227. // (unsigned) hexadecimal
  228. case 'x':
  229. num = getuint(&ap, lflag);
  230. base = 16;
  231. number:
  232. printnum(putch, putdat, num, base, width, padc);
  233. break;
  234. // escaped '%' character
  235. case '%':
  236. putch(ch, putdat);
  237. break;
  238. // unrecognized escape sequence - just print it literally
  239. default:
  240. putch('%', putdat);
  241. for (fmt --; fmt[-1] != '%'; fmt --)
  242. /* do nothing */;
  243. break;
  244. }
  245. }
  246. }
  247. /* sprintbuf is used to save enough information of a buffer */
  248. struct sprintbuf {
  249. char *buf; // address pointer points to the first unused memory
  250. char *ebuf; // points the end of the buffer
  251. int cnt; // the number of characters that have been placed in this buffer
  252. };
  253. /* *
  254. * sprintputch - 'print' a single character in a buffer
  255. * @ch: the character will be printed
  256. * @b: the buffer to place the character @ch
  257. * */
  258. static void
  259. sprintputch(int ch, struct sprintbuf *b) {
  260. b->cnt ++;
  261. if (b->buf < b->ebuf) {
  262. *b->buf ++ = ch;
  263. }
  264. }
  265. /* *
  266. * snprintf - format a string and place it in a buffer
  267. * @str: the buffer to place the result into
  268. * @size: the size of buffer, including the trailing null space
  269. * @fmt: the format string to use
  270. * */
  271. int
  272. snprintf(char *str, size_t size, const char *fmt, ...) {
  273. va_list ap;
  274. int cnt;
  275. va_start(ap, fmt);
  276. cnt = vsnprintf(str, size, fmt, ap);
  277. va_end(ap);
  278. return cnt;
  279. }
  280. /* *
  281. * vsnprintf - format a string and place it in a buffer, it's called with a va_list
  282. * instead of a variable number of arguments
  283. * @str: the buffer to place the result into
  284. * @size: the size of buffer, including the trailing null space
  285. * @fmt: the format string to use
  286. * @ap: arguments for the format string
  287. *
  288. * The return value is the number of characters which would be generated for the
  289. * given input, excluding the trailing '\0'.
  290. *
  291. * Call this function if you are already dealing with a va_list.
  292. * Or you probably want snprintf() instead.
  293. * */
  294. int
  295. vsnprintf(char *str, size_t size, const char *fmt, va_list ap) {
  296. struct sprintbuf b = {str, str + size - 1, 0};
  297. if (str == NULL || b.buf > b.ebuf) {
  298. return -E_INVAL;
  299. }
  300. // print the string to the buffer
  301. vprintfmt((void*)sprintputch, &b, fmt, ap);
  302. // null terminate the buffer
  303. *b.buf = '\0';
  304. return b.cnt;
  305. }