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

359 lines
10 KiB

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