DragonOS/user/libs/libc/printf.c
login d328bfce6b
bugfix: 修正潜在的错误路径 (#64)
* bugfix: 修正潜在的错误路径

* 修正格式
2022-10-22 20:17:40 +08:00

554 lines
14 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "printf.h"
#include <libc/math.h>
#include <libc/stdio.h>
#include <libc/stdlib.h>
#include <libc/string.h>
#include <libsystem/syscall.h>
static char *write_num(char *str, uint64_t num, int base, int field_width, int precision, int flags);
static char *write_float_point_num(char *str, double num, int field_width, int precision, int flags);
static int skip_and_atoi(const char **s)
{
/**
* @brief 获取连续的一段字符对应整数的值
* @param:**s 指向 指向字符串的指针 的指针
*/
int ans = 0;
while (is_digit(**s))
{
ans = ans * 10 + (**s) - '0';
++(*s);
}
return ans;
}
/**
* @brief 往屏幕上输出字符串
*
* @param str 字符串指针
* @param front_color 前景色
* @param bg_color 背景色
* @return int64_t
*/
int64_t put_string(char *str, uint64_t front_color, uint64_t bg_color)
{
return syscall_invoke(SYS_PUT_STRING, (uint64_t)str, front_color, bg_color, 0, 0, 0, 0, 0);
}
int printf(const char *fmt, ...)
{
char buf[4096];
int count = 0;
va_list args;
va_start(args, fmt);
count = vsprintf(buf, fmt, args);
va_end(args);
put_string(buf, COLOR_WHITE, COLOR_BLACK);
return count;
}
int sprintf(char *buf, const char *fmt, ...)
{
int count = 0;
va_list args;
va_start(args, fmt);
count = vsprintf(buf, fmt, args);
va_end(args);
return count;
}
/**
* 将字符串按照fmt和args中的内容进行格式化然后保存到buf中
* @param buf 结果缓冲区
* @param fmt 格式化字符串
* @param args 内容
* @return 最终字符串的长度
*/
int vsprintf(char *buf, const char *fmt, va_list args)
{
// 当需要输出的字符串的指针为空时,使用该字符填充目标字符串的指针
static const char __end_zero_char = '\0';
char *str = NULL, *s = NULL;
str = buf;
int flags; // 用来存储格式信息的bitmap
int field_width; //区域宽度
int precision; //精度
int qualifier; //数据显示的类型
int len;
//开始解析字符串
for (; *fmt; ++fmt)
{
//内容不涉及到格式化,直接输出
if (*fmt != '%')
{
*str = *fmt;
++str;
continue;
}
//开始格式化字符串
//清空标志位和field宽度
field_width = flags = 0;
bool flag_tmp = true;
bool flag_break = false;
++fmt;
while (flag_tmp)
{
switch (*fmt)
{
case '\0':
//结束解析
flag_break = true;
flag_tmp = false;
break;
case '-':
// 左对齐
flags |= LEFT;
++fmt;
break;
case '+':
//在正数前面显示加号
flags |= PLUS;
++fmt;
break;
case ' ':
flags |= SPACE;
++fmt;
break;
case '#':
//在八进制数前面显示 '0o',在十六进制数前面显示 '0x' 或 '0X'
flags |= SPECIAL;
++fmt;
break;
case '0':
//显示的数字之前填充0来取代空格
flags |= PAD_ZERO;
++fmt;
break;
default:
flag_tmp = false;
break;
}
}
if (flag_break)
break;
//获取区域宽度
field_width = -1;
if (*fmt == '*')
{
field_width = va_arg(args, int);
++fmt;
}
else if (is_digit(*fmt))
{
field_width = skip_and_atoi(&fmt);
if (field_width < 0)
{
field_width = -field_width;
flags |= LEFT;
}
}
//获取小数精度
precision = -1;
if (*fmt == '.')
{
++fmt;
if (*fmt == '*')
{
precision = va_arg(args, int);
++fmt;
}
else if is_digit (*fmt)
{
precision = skip_and_atoi(&fmt);
}
}
//获取要显示的数据的类型
if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || *fmt == 'Z')
{
qualifier = *fmt;
++fmt;
}
//为了支持lld
if (qualifier == 'l' && *fmt == 'l', *(fmt + 1) == 'd')
++fmt;
//转化成字符串
long long *ip;
switch (*fmt)
{
//输出 %
case '%':
*str++ = '%';
break;
// 显示一个字符
case 'c':
//靠右对齐
if (!(flags & LEFT))
{
while (--field_width > 0)
{
*str = ' ';
++str;
}
}
*str++ = (unsigned char)va_arg(args, int);
while (--field_width > 0)
{
*str = ' ';
++str;
}
break;
//显示一个字符串
case 's':
s = va_arg(args, char *);
if (!s)
s = &__end_zero_char;
len = strlen(s);
if (precision < 0)
{
//未指定精度
precision = len;
}
else if (len > precision)
{
len = precision;
}
//靠右对齐
if (!(flags & LEFT))
while (len < field_width--)
{
*str = ' ';
++str;
}
for (int i = 0; i < len; i++)
{
*str = *s;
++s;
++str;
}
while (len < field_width--)
{
*str = ' ';
++str;
}
break;
//以八进制显示字符串
case 'o':
flags |= SMALL;
case 'O':
flags |= SPECIAL;
if (qualifier == 'l')
str = write_num(str, va_arg(args, long long), 8, field_width, precision, flags);
else
str = write_num(str, va_arg(args, int), 8, field_width, precision, flags);
break;
//打印指针指向的地址
case 'p':
if (field_width == 0)
{
field_width = 2 * sizeof(void *);
flags |= PAD_ZERO;
}
str = write_num(str, (unsigned long)va_arg(args, void *), 16, field_width, precision, flags);
break;
//打印十六进制
case 'x':
flags |= SMALL;
case 'X':
// flags |= SPECIAL;
if (qualifier == 'l')
str = write_num(str, va_arg(args, int64_t), 16, field_width, precision, flags);
else
str = write_num(str, va_arg(args, int), 16, field_width, precision, flags);
break;
//打印十进制有符号整数
case 'i':
case 'd':
flags |= SIGN;
if (qualifier == 'l')
str = write_num(str, va_arg(args, long long), 10, field_width, precision, flags);
else
str = write_num(str, va_arg(args, int), 10, field_width, precision, flags);
break;
//打印十进制无符号整数
case 'u':
if (qualifier == 'l')
str = write_num(str, va_arg(args, unsigned long long), 10, field_width, precision, flags);
else
str = write_num(str, va_arg(args, unsigned int), 10, field_width, precision, flags);
break;
//输出有效字符数量到*ip对应的变量
case 'n':
if (qualifier == 'l')
ip = va_arg(args, long long *);
else
ip = (int64_t *)va_arg(args, int *);
*ip = str - buf;
break;
case 'f':
// 默认精度为3
if (precision < 0)
precision = 3;
str = write_float_point_num(str, va_arg(args, double), field_width, precision, flags);
break;
//对于不识别的控制符,直接输出
default:
*str++ = '%';
if (*fmt)
*str++ = *fmt;
else
--fmt;
break;
}
}
*str = '\0';
//返回缓冲区已有字符串的长度。
return str - buf;
}
static char *write_num(char *str, uint64_t num, int base, int field_width, int precision, int flags)
{
/**
* @brief 将数字按照指定的要求转换成对应的字符串
*
* @param str 要返回的字符串
* @param num 要打印的数值
* @param base 基数
* @param field_width 区域宽度
* @param precision 精度
* @param flags 标志位
*/
// 首先判断是否支持该进制
if (base < 2 || base > 36)
return 0;
char pad, sign, tmp_num[100];
const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 显示小写字母
if (flags & SMALL)
digits = "0123456789abcdefghijklmnopqrstuvwxyz";
if (flags & LEFT)
flags &= ~PAD_ZERO;
// 设置填充元素
pad = (flags & PAD_ZERO) ? '0' : ' ';
sign = 0;
if (flags & SIGN)
{
int64_t signed_num = (int64_t)num;
if (signed_num < 0)
{
sign = '-';
num = -signed_num;
}
else
num = signed_num;
}
else
{
// 设置符号
sign = (flags & PLUS) ? '+' : ((flags & SPACE) ? ' ' : 0);
}
// sign占用了一个宽度
if (sign)
--field_width;
if (flags & SPECIAL)
if (base == 16) // 0x占用2个位置
field_width -= 2;
else if (base == 8) // O占用一个位置
--field_width;
int js_num = 0; // 临时数字字符串tmp_num的长度
if (num == 0)
tmp_num[js_num++] = '0';
else
{
num = llabs(num);
//进制转换
while (num > 0)
{
tmp_num[js_num++] = digits[num % base]; // 注意这里,输出的数字,是小端对齐的。低位存低位
num /= base;
}
}
if (js_num > precision)
precision = js_num;
field_width -= precision;
// 靠右对齐
if (!(flags & (LEFT + PAD_ZERO)))
while (field_width-- > 0)
*str++ = ' ';
if (sign)
*str++ = sign;
if (flags & SPECIAL)
if (base == 16)
{
*str++ = '0';
*str++ = digits[33];
}
else if (base == 8)
*str++ = digits[24]; //注意这里是英文字母O或者o
if (!(flags & LEFT))
while (field_width-- > 0)
*str++ = pad;
while (js_num < precision)
{
--precision;
*str++ = '0';
}
while (js_num-- > 0)
*str++ = tmp_num[js_num];
while (field_width-- > 0)
*str++ = ' ';
return str;
}
static char *write_float_point_num(char *str, double num, int field_width, int precision, int flags)
{
/**
* @brief 将浮点数按照指定的要求转换成对应的字符串
*
* @param str 要返回的字符串
* @param num 要打印的数值
* @param field_width 区域宽度
* @param precision 精度
* @param flags 标志位
*/
char pad, sign, tmp_num_z[100], tmp_num_d[350];
const char *digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// 显示小写字母
if (flags & SMALL)
digits = "0123456789abcdefghijklmnopqrstuvwxyz";
// 设置填充元素
pad = (flags & PAD_ZERO) ? '0' : ' ';
sign = 0;
if (flags & SIGN && num < 0)
{
sign = '-';
num = -num;
}
else
{
// 设置符号
sign = (flags & PLUS) ? '+' : ((flags & SPACE) ? ' ' : 0);
}
// sign占用了一个宽度
if (sign)
--field_width;
int js_num_z = 0, js_num_d = 0; // 临时数字字符串tmp_num_z tmp_num_d的长度
uint64_t num_z = (uint64_t)(num); // 获取整数部分
uint64_t num_decimal = (uint64_t)(round(1.0 * (num - num_z) * pow(10, precision))); // 获取小数部分
if (num == 0 || num_z == 0)
tmp_num_z[js_num_z++] = '0';
else
{
//存储整数部分
while (num_z > 0)
{
tmp_num_z[js_num_z++] = digits[num_z % 10]; // 注意这里,输出的数字,是小端对齐的。低位存低位
num_z /= 10;
}
}
while (num_decimal > 0)
{
tmp_num_d[js_num_d++] = digits[num_decimal % 10];
num_decimal /= 10;
}
field_width -= (precision + 1 + js_num_z);
// 靠右对齐
if (!(flags & LEFT))
while (field_width-- > 0)
*str++ = pad;
if (sign)
*str++ = sign;
// 输出整数部分
// while (js_num_z-- > 0)
// *str++ = tmp_num_z[js_num_z];
while (js_num_z > 0)
{
*str++ = tmp_num_z[js_num_z - 1];
--js_num_z;
}
*str++ = '.';
// 输出小数部分
int total_dec_count = js_num_d;
for (int i = 0; i < precision && js_num_d-- > 0; ++i)
*str++ = tmp_num_d[js_num_d];
while (total_dec_count < precision)
{
++total_dec_count;
*str++ = '0';
}
while (field_width-- > 0)
*str++ = ' ';
return str;
}