/*
 *  linux/kernel/vsprintf.c
 *
 *  Copyright (C) 1991, 1992  Linus Torvalds
 */

/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
/*
 * Wirzenius wrote this portably, Torvalds fucked it up :-)
 */

#include <stdarg.h>
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>

//简单的字符串分隔 字符串到无符号长整型转化
unsigned long simple_strtoul(const char *cp,char **endp,unsigned int base)
{
    unsigned long result = 0,value;

//如果未指明进制
    if (!base) {
        //初始化为10进制
        base = 10;
        //如果cp以0开始则为8进制
        if (*cp == '0') {
            //8进制
            base = 8;
            //下一字符
            cp++;
            //如果是x并且检测第二个字符是否为十六进制数字
            if ((*cp == 'x') && isxdigit(cp[1])) {
                //则为16进制
                cp++;
                base = 16;
            }
        }
    }
    //明确进制,是否为十六进制数字,如果是,那么是数字,还是是字符,处理不同情况后
    while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
        ? toupper(*cp) : *cp)-'A'+10) < base) {
        result = result*base + value;//进行计算
        cp++;
    }
    //判断是否结束转化
    if (endp)
        *endp = (char *)cp;
    return result;//返回结果
}

/* we use this so that we can do without the ctype library */
//判断字符c是否为数字
#define is_digit(c)    ((c) >= '0' && (c) <= '9')
//字符串转为整型,
static int skip_atoi(const char **s)
{
    int i=0;

while (is_digit(**s))
        i = i*10 + *((*s)++) - '0';
    return i;
}

#define ZEROPAD    1        /* pad with zero */                           //用0填补
#define SIGN    2        /* unsigned/signed long */                    //无符号/符号长整型
#define PLUS    4        /* show plus */                               //显示*号
#define SPACE    8        /* space if plus */                           //如果+号,则空格
#define LEFT    16        /* left justified */                          //左侧调整
#define SPECIAL    32        /* 0x */                                      //0x
#define SMALL    64        /* use 'abcdef' instead of 'ABCDEF' */        //小写替代大写

//除操作
#define do_div(n,base) ({ \
int __res; \
__asm__("divl %4":"=a" (n),"=d" (__res):"0" (n),"1" (0),"r" (base)); \
__res; })

//将字符转为指定进制的字符串
static char * number(char * str, int num, int base, int size, int precision
    ,int type)
{
    char c,sign,tmp[36];
    const char *digits="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    int i;
    //根据类型type决定格式
    if (type&SMALL) digits="0123456789abcdefghijklmnopqrstuvwxyz";//是否使用小写
    if (type&LEFT) type &= ~ZEROPAD;                              //左侧是否填零
    if (base<2 || base>36)                                        //进制判断
        return 0;
    c = (type & ZEROPAD) ? '0' : ' ' ;                            //如果用0填补,则补零,否则用空格
    if (type&SIGN && num<0) {                                     //格式要求有符号输出,并且小于0
        sign='-';                                                 //负号
        num = -num;
    } else
        sign=(type&PLUS) ? '+' : ((type&SPACE) ? ' ' : 0);        //正数
    if (sign) size--;                                             //如果带符号,则宽度减1
    if (type&SPECIAL)                                             //如果是特殊转换,16进制宽度减2,8进制减1
        if (base==16) size -= 2;
        else if (base==8) size--;
    i=0;                                                          //
    if (num==0)                                                   //如果num是0
        tmp[i++]='0';                                             //结果
    else while (num!=0)                                           //如果不是0
        tmp[i++]=digits[do_div(num,base)];                        //根据进制转化为字符形式
    if (i>precision) precision=i;                                 //确定精度
    size -= precision;
    if (!(type&(ZEROPAD+LEFT)))                                   //不是补零和左对齐
        while(size-->0)
            *str++ = ' ';
    if (sign)
        *str++ = sign;
    if (type&SPECIAL)                                             //处理不同进制的输出前缀
        if (base==8)
            *str++ = '0';
        else if (base==16) {
            *str++ = '0';
            *str++ = digits[33];
        }
    if (!(type&LEFT))                                            //如果不是左对齐
        while(size-->0)
            *str++ = c;
    while(i<precision--)                                         //i为数值num的长度,如果此长度小于指定精度,则补零
        *str++ = '0';
    while(i-->0)                                                 //将转换好的数据填入str中
        *str++ = tmp[i];
    while(size-->0)                                             //左对齐,空格补齐
        *str++ = ' ';
    return str;
}

//格式化字符串
int vsprintf(char *buf, const char *fmt, va_list args)
{
    int len;
    int i;
    char * str;
    char *s;
    int *ip;

int flags;        /* flags to number() */

int field_width;    /* width of output field */
    int precision;        /* min. # of digits for integers; max
                   number of chars for from string */
    int qualifier;        /* 'h', 'l', or 'L' for integer fields */

//首先将字符指针指向buf,然后扫描格式字符串,对各个格式转换进行相应的处理
    for (str=buf ; *fmt ; ++fmt) {
        //从fmt中扫描%,寻找格式转换字符串的开始,如果不是格式指示的字符,则直接依次放入str中
        if (*fmt != '%') {
            *str++ = *fmt;
            continue;
        }
            
        /* process flags */
        //获取格式指示的标志类型
        flags = 0;
        repeat:
            ++fmt;        /* this also skips first '%' */
            switch (*fmt) {
                case '-': flags |= LEFT; goto repeat;
                case '+': flags |= PLUS; goto repeat;
                case ' ': flags |= SPACE; goto repeat;
                case '#': flags |= SPECIAL; goto repeat;
                case '0': flags |= ZEROPAD; goto repeat;
                }
        //确定转换宽度
        /* get field width */
        field_width = -1;
        if (is_digit(*fmt))
            field_width = skip_atoi(&fmt);
        else if (*fmt == '*') {
            /* it's the next argument */
            //这里有bug,需要加入一行代码 ++fmt;
            field_width = va_arg(args, int);
            if (field_width < 0) {
                field_width = -field_width;
                flags |= LEFT;
            }
        }
        //确定转换精读
        /* get the precision */
        precision = -1;
        if (*fmt == '.') {
            ++fmt;    
            if (is_digit(*fmt))
                precision = skip_atoi(&fmt);
            else if (*fmt == '*') {
                /* it's the next argument */
                precision = va_arg(args, int);
            }
            if (precision < 0)
                precision = 0;
        }
        //获取长度修饰
        /* get the conversion qualifier */
        qualifier = -1;
        if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
            qualifier = *fmt;
            ++fmt;
        }
        //开始分析转换指示符
        switch (*fmt) {
        case 'c'://表示对应的参数是字符
            if (!(flags & LEFT)) //是否左对齐
                while (--field_width > 0)
                    *str++ = ' ';
            *str++ = (unsigned char) va_arg(args, int);
            while (--field_width > 0)
                *str++ = ' ';
            break;
        //对应参数是字符串
        case 's':
            s = va_arg(args, char *);
            if (!s)
                s = "<NULL>";
            len = strlen(s);
            if (precision < 0)
                precision = len;
            else if (len > precision)
                len = precision;

if (!(flags & LEFT))
                while (len < field_width--)
                    *str++ = ' ';
            for (i = 0; i < len; ++i)
                *str++ = *s++;
            while (len < field_width--)
                *str++ = ' ';
            break;
        //对应参数是8进制字符
        case 'o':
            str = number(str, va_arg(args, unsigned long), 8,
                field_width, precision, flags);
            break;
        //对应参数是指针类型
        case 'p':
            if (field_width == -1) {
                field_width = 8;
                flags |= ZEROPAD;
            }
            str = number(str,
                (unsigned long) va_arg(args, void *), 16,
                field_width, precision, flags);
            break;
        //对应参数是16进制,x是小写格式,X是大写格式
        case 'x':
            flags |= SMALL;
        case 'X':
            str = number(str, va_arg(args, unsigned long), 16,
                field_width, precision, flags);
            break;
        //参数是整数的处理
        case 'd':
        case 'i':
            flags |= SIGN;
        case 'u':
            str = number(str, va_arg(args, unsigned long), 10,
                field_width, precision, flags);
            break;
        //如果指示符是n,即\n,则表示要把目前为止转换输出字符保存到对应参数指针指定的位置中
        case 'n':
            ip = va_arg(args, int *);
            *ip = (str - buf);
            break;
        //如果不是以%,则转换格式出错,处理后退出
        default:
            if (*fmt != '%')
                *str++ = '%';
            if (*fmt)
                *str++ = *fmt;
            else
                --fmt;
            break;
        }
    }
    //字符串结束符
    *str = '\0';
    return str-buf;//长度
}

//格式化输出
int sprintf(char * buf, const char *fmt, ...)
{
    //变长参数
    va_list args;
    int i;

va_start(args, fmt);
    i=vsprintf(buf,fmt,args);
    va_end(args);
    return i;
}

kernel/vsprintf.c的更多相关文章

  1. Linux0.11内核剖析--初始化程序(init)

    1.概述 在内核源代码的 init/目录中只有一个 main.c 文件. 系统在执行完 boot/目录中的 head.s 程序后就会将执行权交给 main.c.该程序虽然不长,但却包括了内核初始化的所 ...

  2. kernel/printk.c

    /* *  linux/kernel/printk.c * *  Copyright (C) 1991, 1992  Linus Torvalds * * Modified to make sys_s ...

  3. kernel/panic.c

    /* *  linux/kernel/panic.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ /* * This function is us ...

  4. kernel/Makefile

    ## Makefile for the linux kernel.## Note! Dependencies are done automagically by 'make dep', which a ...

  5. Linux 内核概述 - Linux Kernel

    Linux 内核学习笔记整理. Unix unix 已有40历史,但计算机科学家仍认为其是现存操作系统中最大和最优秀的系统,它已成为一种传奇的存在,历经时间的考验却依然声名不坠. 1973 年,在用 ...

  6. 04.ubuntu下kvm 命令行安装64位ubuntu报"Couldn't find hvm kernel for Ubuntu tree."的问题

    1.安装ubuntu时使用的virt-install的配置: virt-install \ --name test4 \ --ram 1024 \ --disk path=/data/01_ubunt ...

  7. 在 kernel 下打 log。 怪異現象與解決方式。

    code battery_log(BAT_LOG_CRTI, "y t: %d \n", (int)my_timer_timeout); battery_log(BAT_LOG_C ...

  8. 机器学习——支持向量机(SVM)之核函数(kernel)

    对于线性不可分的数据集,可以利用核函数(kernel)将数据转换成易于分类器理解的形式. 如下图,如果在x轴和y轴构成的坐标系中插入直线进行分类的话, 不能得到理想的结果,或许我们可以对圆中的数据进行 ...

  9. User space 与 Kernel space

    学习 Linux 时,经常可以看到两个词:User space(用户空间)和 Kernel space(内核空间). 简单说,Kernel space 是 Linux 内核的运行空间,User spa ...

随机推荐

  1. mysql连接查询和子查询

    一.连接查询 1.交叉连接 就是从一张表的一条记录去连接另一张表中的所有记录,并且保存所有的记录,其中包括两个表的所有的字段! 从结果上看,就是对两张表做笛卡尔积! 笛卡尔积也就是两个表中所有可能的连 ...

  2. 黑马程序员——C语言基础 枚举 宏定义 自定义 static exterm

    Java培训.Android培训.iOS培训..Net培训.期待与您交流! (以下内容是对黑马苹果入学视频的个人知识点总结) (一)枚举 1)枚举类型的定义 枚举是C语言中的一种基本数据类型,并不是构 ...

  3. break continue 区别 以及实例

    不论是MATLAB.c/c++.c#还是其他类型的编程语言,我们总是避免不了和for循环以及switch语句打交道,而对循环进行优化的时候,又总是避免不了用到break以及continue来控制循环, ...

  4. Unreal Engine Plugin management

    Be aware to remove any dependencies to any modules related to Editor, or else it will end up with fa ...

  5. Linux网络编程(多人在线聊天系统)

    一.首先是服务器的建立 首先是一个信号终止程序,发信号ctrl+c终止程序,而是是初始化网络通信. 创建一个描述符负责绑定服务器和监听服务器接收客户端的消息. socket()->sockadd ...

  6. 学习Find函数和select

    Find函数其实就类似于在excel按下Ctrl+F出现的查找功能:在某个区域中查找你要找的字符,一旦找到就定位到第一个对应的单元格.所以Find函数的返回值是个单元格,也就是个range值.举例,s ...

  7. asp.net mvc api auth

    一.登录 /// <summary> /// 获取令牌 /// </summary> /// <param name="userName">用户 ...

  8. IOS线程学习(一)

    1.NSThread  官方的描述 An NSThread object controls a thread of execution. Use this class when you want to ...

  9. Unable to load native-hadoop library for your platform

    #HADOOP VARIABLES START export JAVA_HOME=/home/yang/jdk1.7.0_80export HADOOP_HOME=/home/hadoop/hadoo ...

  10. UE4 中在 Actor 中动态 Create Component 与ChildActor 的 小笔记

    Note:旧版本的UE4 的Attach 和12.13版本有些不一样 创建Component: UCpp_MyComponent* temp_imageCom = NewObject<UCpp_ ...