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

343 lines
9.6 KiB

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