// Stripped-down primitive printf-style formatting routines, // used in common by printf, sprintf, fprintf, etc. // This code is also used by both the kernel and user programs. #include #include #include #include #include /* * Space or zero padding and a field width are supported for the numeric * formats only. * * The special format %e takes an integer error code * and prints a string describing the error. * The integer may be positive or negative, * so that -E_NO_MEM and E_NO_MEM are equivalent. */ static const char * const error_string[MAXERROR] = { [E_UNSPECIFIED] = "unspecified error", [E_BAD_ENV] = "bad environment", [E_INVAL] = "invalid parameter", [E_NO_MEM] = "out of memory", [E_NO_FREE_ENV] = "out of environments", [E_FAULT] = "segmentation fault", }; /* * Print a number (base <= 16) in reverse order, * using specified putch function and associated pointer putdat. */ static void printnum(void (*putch)(int, void*), void *putdat, unsigned long long num, unsigned base, int width, int padc) { // first recursively print all preceding (more significant) digits if (num >= base) { printnum(putch, putdat, num / base, base, width - 1, padc); } else { // print any needed pad characters before first digit while (--width > 0) putch(padc, putdat); } // then print this (the least significant) digit putch("0123456789abcdef"[num % base], putdat); } // Get an unsigned int of various possible sizes from a varargs list, // depending on the lflag parameter. static unsigned long long getuint(va_list *ap, int lflag) { if (lflag >= 2) return va_arg(*ap, unsigned long long); else if (lflag) return va_arg(*ap, unsigned long); else return va_arg(*ap, unsigned int); } // Same as getuint but signed - can't use getuint // because of sign extension static long long getint(va_list *ap, int lflag) { if (lflag >= 2) return va_arg(*ap, long long); else if (lflag) return va_arg(*ap, long); else return va_arg(*ap, int); } // Main function to format and print a string. void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...); void vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap) { register const char *p; register int ch, err; unsigned long long num; int base, lflag, width, precision, altflag; char padc; while (1) { while ((ch = *(unsigned char *) fmt++) != '%') { //遍历输入的第一个参数,即输出信息的格式,先把格式字符串中'%'之前的字符一个个输出,因为它们前面没有'%',所以它们就是要直接显示在屏幕上的 if (ch == '\0') //当然中间如果遇到'\0',代表这个字符串的访问结束 return; putch(ch, putdat); //调用putch函数,把一个字符ch输出到putdat指针所指向的地址中所存放的值对应的地址处 } // Process a %-escape sequence //处理'%'后面的格式化输出 padc = ' '; //对齐方式标志位 width = -1; //整数部分有效数字位数 precision = -1; //小数部分有效数字位数 lflag = 0; altflag = 0; reswitch: switch (ch = *(unsigned char *) fmt++) { //根据位于'%'后面的第一个字符进行分情况处理 // flag to pad on the right case '-': //%后面的'-'代表要进行左对齐输出,右边填空格,如果省略代表右对齐 padc = '-'; //如果有这个字符代表左对齐,则把对齐方式标志位变为'-' goto reswitch; //处理下一个字符 // flag to pad with 0's instead of spaces case '0': //0--有0表示进行对齐输出时填0,如省略表示填入空格,并且如果为0,则一定是右对齐 padc = '0'; //对其方式标志位变为0 goto reswitch; // width field //遇到指定输出位数标志 case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for (precision = 0; ; ++fmt) { //把遇到的位数字符串转换为真实的位数,比如输入的'%40',代表有效位数为40位,下面的循环就是把precesion的值设置为40 precision = precision * 10 + ch - '0'; ch = *fmt; if (ch < '0' || ch > '9') break; } goto process_precision; //跳转到process_precistion子过程 case '*': //*--代表有效数字的位数也是由输入参数指定的,比如printf("%*.*f", 10, 2, n),其中10,2就是用来指定显示的有效数字位数的 precision = va_arg(ap, int); goto process_precision; case '.': //如果遇到'.',代表有小数字段 if (width < 0) //代表有小数点,但是小数点前面并没有数字,比如'%.6f'这种情况,此时代表整数部分全部显示 width = 0; goto reswitch; case '#': altflag = 1; goto reswitch; process_precision: //处理输出精度,把width字段赋值为刚刚计算出来的precision值,所以width应该是整数部分的有效数字位数 if (width < 0) width = precision, precision = -1; goto reswitch; // long flag (doubled for long long) case 'l': //如果遇到'l',代表应该是输入long类型,如果有两个'l'代表long long lflag++; //此时把lflag++ goto reswitch; // character case 'c': //如果是'c'代表显示一个字符 putch(va_arg(ap, int), putdat); //调用输出一个字符到内存的函数putch break; // error message case 'e': err = va_arg(ap, int); if (err < 0) err = -err; if (err >= MAXERROR || (p = error_string[err]) == NULL) printfmt(putch, putdat, "error %d", err); else printfmt(putch, putdat, "%s", p); break; // string case 's': if ((p = va_arg(ap, char *)) == NULL) p = "(null)"; if (width > 0 && padc != '-') for (width -= strnlen(p, precision); width > 0; width--) putch(padc, putdat); for (; (ch = *p++) != '\0' && (precision < 0 || --precision >= 0); width--) if (altflag && (ch < ' ' || ch > '~')) putch('?', putdat); else putch(ch, putdat); for (; width > 0; width--) putch(' ', putdat); break; // (signed) decimal case 'd': num = getint(&ap, lflag); if ((long long) num < 0) { putch('-', putdat); num = -(long long) num; } base = 10; goto number; // unsigned decimal case 'u': num = getuint(&ap, lflag); base = 10; goto number; // (unsigned) octal case 'o': // Replace this with your code. putch('0', putdat); num = getuint(&ap, lflag); base = 8; goto number; // pointer case 'p': putch('0', putdat); putch('x', putdat); num = (unsigned long long) (uintptr_t) va_arg(ap, void *); base = 16; goto number; // (unsigned) hexadecimal case 'x': num = getuint(&ap, lflag); base = 16; number: printnum(putch, putdat, num, base, width, padc); break; // escaped '%' character case '%': putch(ch, putdat); break; // unrecognized escape sequence - just print it literally default: putch('%', putdat); for (fmt--; fmt[-1] != '%'; fmt--) /* do nothing */; break; } } } void printfmt(void (*putch)(int, void*), void *putdat, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vprintfmt(putch, putdat, fmt, ap); va_end(ap); } struct sprintbuf { char *buf; char *ebuf; int cnt; }; static void sprintputch(int ch, struct sprintbuf *b) { b->cnt++; if (b->buf < b->ebuf) *b->buf++ = ch; } int vsnprintf(char *buf, int n, const char *fmt, va_list ap) { struct sprintbuf b = {buf, buf+n-1, 0}; if (buf == NULL || n < 1) return -E_INVAL; // print the string to the buffer vprintfmt((void*)sprintputch, &b, fmt, ap); // null terminate the buffer *b.buf = '\0'; return b.cnt; } int snprintf(char *buf, int n, const char *fmt, ...) { va_list ap; int rc; va_start(ap, fmt); rc = vsnprintf(buf, n, fmt, ap); va_end(ap); return rc; }