// Stripped-down primitive printf-style formatting routines,
//原生的不同格式输出的routines(可以理解为api)
// used in common by printf, sprintf, fprintf, etc.
//常用于printf, sprintf, fprintf, etc
// This code is also used by both the kernel and user programs.
//这段代码也用于内核和用户程序
//依赖关系 printfmt ——> printf ——> console
#include <inc/types.h>
#include <inc/stdio.h>
#include <inc/string.h>
#include <inc/stdarg.h>
#include <inc/error.h>
/*
* Space or zero padding and a field width are supported for the numeric
* formats only.
* 仅 numeric 格式支持空格或零填充以及字段宽度。
* The special format %e takes an integer error code
* and prints a string describing the error.
* 特殊格式%e接受一个整数错误码并打印一个描述错误的字符串。
* The integer may be positive or negative,
* so that -E_NO_MEM and E_NO_MEM are equivalent.
* 整数可以是正的也可以是负的,因此-E_NO_MEM和E_NO_MEM是等效的。
*/
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.(base <= 16)
* 使用指定的putch函数和相关的指针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.
// 根据lflag参数,从varargs list 中获取不同大小的unsigned int。
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
//与getuint相同,但已签名-由于sign extension,不能使用getuint
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, ...);
/*
* 例子cprintf("x %d, y %x, z %d\n", x, y, z);
*
* *putdat 实际调用的是kern/console.c的 cputchar 作用是output a character to the console
*
* *fmt 相当于"x %d, y %x, z %d\n"
*
* ap 可变参数,相当于x, y, z
*/
void
vprintfmt(void (*putch)(int, void*), void *putdat, const char *fmt, va_list ap)
{
//register修饰符暗示编译程序相应的变量将被频繁地使用,
//如果可能的话,应将其保存在CPU的寄存器中,以加快其存储速度
register const char *p;
register int ch, err;
//unsigned的作用就是将数字类型无符号化。
//例如 int 型的范围:-2^31 ~ 2^31 - 1,而unsigned int的范围:0 ~ 2^32。
unsigned long long num;
int base, lflag, width, precision, altflag;
char padc;
//遍历输入的第一个参数,即格式化字符串
//先把格式字符串中'%'之前的字符一个个输出,因为它们前面没有'%',
//所以它们就是要直接显示在屏幕上的
//中间如果遇到'\0',代表这个字符串的访问结束
while (1) {
while ((ch = *(unsigned char *) fmt++) != '%') {
if (ch == '\0')
return;
putch(ch, putdat); //putdat一开始为0,每输出一个字符,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':
padc = '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) {
precision = precision * 10 + ch - '0';
ch = *fmt;
if (ch < '0' || ch > '9')
break;
}
goto process_precision;
case '*':
precision = va_arg(ap, int);
goto process_precision;
case '.':
if (width < 0)
width = 0;
goto reswitch;
case '#':
altflag = 1;
goto reswitch;
process_precision:
if (width < 0)
width = precision, precision = -1;
goto reswitch;
// long flag (doubled for long long)
case 'l':
lflag++;
goto reswitch;
// character
case 'c':
putch(va_arg(ap, int), putdat);
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('X', putdat);
putch('X', putdat);
putch('X', putdat);
break;*/
num = getuint(&ap, lflag);
base = 6;
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;
}