linux/arch/x86/boot/printf.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/* -*- linux-c -*- ------------------------------------------------------- *
   3 *
   4 *   Copyright (C) 1991, 1992 Linus Torvalds
   5 *   Copyright 2007 rPath, Inc. - All Rights Reserved
   6 *
   7 * ----------------------------------------------------------------------- */
   8
   9/*
  10 * Oh, it's a waste of space, but oh-so-yummy for debugging.  This
  11 * version of printf() does not include 64-bit support.  "Live with
  12 * it."
  13 *
  14 */
  15
  16#include "boot.h"
  17
  18static int skip_atoi(const char **s)
  19{
  20        int i = 0;
  21
  22        while (isdigit(**s))
  23                i = i * 10 + *((*s)++) - '0';
  24        return i;
  25}
  26
  27#define ZEROPAD 1               /* pad with zero */
  28#define SIGN    2               /* unsigned/signed long */
  29#define PLUS    4               /* show plus */
  30#define SPACE   8               /* space if plus */
  31#define LEFT    16              /* left justified */
  32#define SMALL   32              /* Must be 32 == 0x20 */
  33#define SPECIAL 64              /* 0x */
  34
  35#define __do_div(n, base) ({ \
  36int __res; \
  37__res = ((unsigned long) n) % (unsigned) base; \
  38n = ((unsigned long) n) / (unsigned) base; \
  39__res; })
  40
  41static char *number(char *str, long num, int base, int size, int precision,
  42                    int type)
  43{
  44        /* we are called with base 8, 10 or 16, only, thus don't need "G..."  */
  45        static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
  46
  47        char tmp[66];
  48        char c, sign, locase;
  49        int i;
  50
  51        /* locase = 0 or 0x20. ORing digits or letters with 'locase'
  52         * produces same digits or (maybe lowercased) letters */
  53        locase = (type & SMALL);
  54        if (type & LEFT)
  55                type &= ~ZEROPAD;
  56        if (base < 2 || base > 16)
  57                return NULL;
  58        c = (type & ZEROPAD) ? '0' : ' ';
  59        sign = 0;
  60        if (type & SIGN) {
  61                if (num < 0) {
  62                        sign = '-';
  63                        num = -num;
  64                        size--;
  65                } else if (type & PLUS) {
  66                        sign = '+';
  67                        size--;
  68                } else if (type & SPACE) {
  69                        sign = ' ';
  70                        size--;
  71                }
  72        }
  73        if (type & SPECIAL) {
  74                if (base == 16)
  75                        size -= 2;
  76                else if (base == 8)
  77                        size--;
  78        }
  79        i = 0;
  80        if (num == 0)
  81                tmp[i++] = '0';
  82        else
  83                while (num != 0)
  84                        tmp[i++] = (digits[__do_div(num, base)] | locase);
  85        if (i > precision)
  86                precision = i;
  87        size -= precision;
  88        if (!(type & (ZEROPAD + LEFT)))
  89                while (size-- > 0)
  90                        *str++ = ' ';
  91        if (sign)
  92                *str++ = sign;
  93        if (type & SPECIAL) {
  94                if (base == 8)
  95                        *str++ = '0';
  96                else if (base == 16) {
  97                        *str++ = '0';
  98                        *str++ = ('X' | locase);
  99                }
 100        }
 101        if (!(type & LEFT))
 102                while (size-- > 0)
 103                        *str++ = c;
 104        while (i < precision--)
 105                *str++ = '0';
 106        while (i-- > 0)
 107                *str++ = tmp[i];
 108        while (size-- > 0)
 109                *str++ = ' ';
 110        return str;
 111}
 112
 113int vsprintf(char *buf, const char *fmt, va_list args)
 114{
 115        int len;
 116        unsigned long num;
 117        int i, base;
 118        char *str;
 119        const char *s;
 120
 121        int flags;              /* flags to number() */
 122
 123        int field_width;        /* width of output field */
 124        int precision;          /* min. # of digits for integers; max
 125                                   number of chars for from string */
 126        int qualifier;          /* 'h', 'l', or 'L' for integer fields */
 127
 128        for (str = buf; *fmt; ++fmt) {
 129                if (*fmt != '%') {
 130                        *str++ = *fmt;
 131                        continue;
 132                }
 133
 134                /* process flags */
 135                flags = 0;
 136              repeat:
 137                ++fmt;          /* this also skips first '%' */
 138                switch (*fmt) {
 139                case '-':
 140                        flags |= LEFT;
 141                        goto repeat;
 142                case '+':
 143                        flags |= PLUS;
 144                        goto repeat;
 145                case ' ':
 146                        flags |= SPACE;
 147                        goto repeat;
 148                case '#':
 149                        flags |= SPECIAL;
 150                        goto repeat;
 151                case '0':
 152                        flags |= ZEROPAD;
 153                        goto repeat;
 154                }
 155
 156                /* get field width */
 157                field_width = -1;
 158                if (isdigit(*fmt))
 159                        field_width = skip_atoi(&fmt);
 160                else if (*fmt == '*') {
 161                        ++fmt;
 162                        /* it's the next argument */
 163                        field_width = va_arg(args, int);
 164                        if (field_width < 0) {
 165                                field_width = -field_width;
 166                                flags |= LEFT;
 167                        }
 168                }
 169
 170                /* get the precision */
 171                precision = -1;
 172                if (*fmt == '.') {
 173                        ++fmt;
 174                        if (isdigit(*fmt))
 175                                precision = skip_atoi(&fmt);
 176                        else if (*fmt == '*') {
 177                                ++fmt;
 178                                /* it's the next argument */
 179                                precision = va_arg(args, int);
 180                        }
 181                        if (precision < 0)
 182                                precision = 0;
 183                }
 184
 185                /* get the conversion qualifier */
 186                qualifier = -1;
 187                if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
 188                        qualifier = *fmt;
 189                        ++fmt;
 190                }
 191
 192                /* default base */
 193                base = 10;
 194
 195                switch (*fmt) {
 196                case 'c':
 197                        if (!(flags & LEFT))
 198                                while (--field_width > 0)
 199                                        *str++ = ' ';
 200                        *str++ = (unsigned char)va_arg(args, int);
 201                        while (--field_width > 0)
 202                                *str++ = ' ';
 203                        continue;
 204
 205                case 's':
 206                        s = va_arg(args, char *);
 207                        len = strnlen(s, precision);
 208
 209                        if (!(flags & LEFT))
 210                                while (len < field_width--)
 211                                        *str++ = ' ';
 212                        for (i = 0; i < len; ++i)
 213                                *str++ = *s++;
 214                        while (len < field_width--)
 215                                *str++ = ' ';
 216                        continue;
 217
 218                case 'p':
 219                        if (field_width == -1) {
 220                                field_width = 2 * sizeof(void *);
 221                                flags |= ZEROPAD;
 222                        }
 223                        str = number(str,
 224                                     (unsigned long)va_arg(args, void *), 16,
 225                                     field_width, precision, flags);
 226                        continue;
 227
 228                case 'n':
 229                        if (qualifier == 'l') {
 230                                long *ip = va_arg(args, long *);
 231                                *ip = (str - buf);
 232                        } else {
 233                                int *ip = va_arg(args, int *);
 234                                *ip = (str - buf);
 235                        }
 236                        continue;
 237
 238                case '%':
 239                        *str++ = '%';
 240                        continue;
 241
 242                        /* integer number formats - set up the flags and "break" */
 243                case 'o':
 244                        base = 8;
 245                        break;
 246
 247                case 'x':
 248                        flags |= SMALL;
 249                case 'X':
 250                        base = 16;
 251                        break;
 252
 253                case 'd':
 254                case 'i':
 255                        flags |= SIGN;
 256                case 'u':
 257                        break;
 258
 259                default:
 260                        *str++ = '%';
 261                        if (*fmt)
 262                                *str++ = *fmt;
 263                        else
 264                                --fmt;
 265                        continue;
 266                }
 267                if (qualifier == 'l')
 268                        num = va_arg(args, unsigned long);
 269                else if (qualifier == 'h') {
 270                        num = (unsigned short)va_arg(args, int);
 271                        if (flags & SIGN)
 272                                num = (short)num;
 273                } else if (flags & SIGN)
 274                        num = va_arg(args, int);
 275                else
 276                        num = va_arg(args, unsigned int);
 277                str = number(str, num, base, field_width, precision, flags);
 278        }
 279        *str = '\0';
 280        return str - buf;
 281}
 282
 283int sprintf(char *buf, const char *fmt, ...)
 284{
 285        va_list args;
 286        int i;
 287
 288        va_start(args, fmt);
 289        i = vsprintf(buf, fmt, args);
 290        va_end(args);
 291        return i;
 292}
 293
 294int printf(const char *fmt, ...)
 295{
 296        char printf_buf[1024];
 297        va_list args;
 298        int printed;
 299
 300        va_start(args, fmt);
 301        printed = vsprintf(printf_buf, fmt, args);
 302        va_end(args);
 303
 304        puts(printf_buf);
 305
 306        return printed;
 307}
 308