root/drivers/acpi/acpica/utprint.c
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
 *
 * Module Name: utprint - Formatted printing routines
 *
 * Copyright (C) 2000 - 2025, Intel Corp.
 *
 *****************************************************************************/

#include <acpi/acpi.h>
#include "accommon.h"

#define _COMPONENT          ACPI_UTILITIES
ACPI_MODULE_NAME("utprint")

#define ACPI_FORMAT_SIGN            0x01
#define ACPI_FORMAT_SIGN_PLUS       0x02
#define ACPI_FORMAT_SIGN_PLUS_SPACE 0x04
#define ACPI_FORMAT_ZERO            0x08
#define ACPI_FORMAT_LEFT            0x10
#define ACPI_FORMAT_UPPER           0x20
#define ACPI_FORMAT_PREFIX          0x40
/* Local prototypes */
static acpi_size
acpi_ut_bound_string_length(const char *string, acpi_size count);

static char *acpi_ut_bound_string_output(char *string, const char *end, char c);

static char *acpi_ut_format_number(char *string,
                                   char *end,
                                   u64 number,
                                   u8 base, s32 width, s32 precision, u8 type);

static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper);

/*******************************************************************************
 *
 * FUNCTION:    acpi_ut_bound_string_length
 *
 * PARAMETERS:  string              - String with boundary
 *              count               - Boundary of the string
 *
 * RETURN:      Length of the string. Less than or equal to Count.
 *
 * DESCRIPTION: Calculate the length of a string with boundary.
 *
 ******************************************************************************/

static acpi_size
acpi_ut_bound_string_length(const char *string, acpi_size count)
{
        u32 length = 0;

        while (*string && count) {
                length++;
                string++;
                count--;
        }

        return (length);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ut_bound_string_output
 *
 * PARAMETERS:  string              - String with boundary
 *              end                 - Boundary of the string
 *              c                   - Character to be output to the string
 *
 * RETURN:      Updated position for next valid character
 *
 * DESCRIPTION: Output a character into a string with boundary check.
 *
 ******************************************************************************/

static char *acpi_ut_bound_string_output(char *string, const char *end, char c)
{

        if (string < end) {
                *string = c;
        }

        ++string;
        return (string);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ut_put_number
 *
 * PARAMETERS:  string              - Buffer to hold reverse-ordered string
 *              number              - Integer to be converted
 *              base                - Base of the integer
 *              upper               - Whether or not using upper cased digits
 *
 * RETURN:      Updated position for next valid character
 *
 * DESCRIPTION: Convert an integer into a string, note that, the string holds a
 *              reversed ordered number without the trailing zero.
 *
 ******************************************************************************/

static char *acpi_ut_put_number(char *string, u64 number, u8 base, u8 upper)
{
        const char *digits;
        u64 digit_index;
        char *pos;

        pos = string;
        digits = upper ? acpi_gbl_upper_hex_digits : acpi_gbl_lower_hex_digits;

        if (number == 0) {
                *(pos++) = '0';
        } else {
                while (number) {
                        (void)acpi_ut_divide(number, base, &number,
                                             &digit_index);
                        *(pos++) = digits[digit_index];
                }
        }

        /* *(Pos++) = '0'; */
        return (pos);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ut_scan_number
 *
 * PARAMETERS:  string              - String buffer
 *              number_ptr          - Where the number is returned
 *
 * RETURN:      Updated position for next valid character
 *
 * DESCRIPTION: Scan a string for a decimal integer.
 *
 ******************************************************************************/

const char *acpi_ut_scan_number(const char *string, u64 *number_ptr)
{
        u64 number = 0;

        while (isdigit((int)*string)) {
                acpi_ut_short_multiply(number, 10, &number);
                number += *(string++) - '0';
        }

        *number_ptr = number;
        return (string);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ut_print_number
 *
 * PARAMETERS:  string              - String buffer
 *              number              - The number to be converted
 *
 * RETURN:      Updated position for next valid character
 *
 * DESCRIPTION: Print a decimal integer into a string.
 *
 ******************************************************************************/

const char *acpi_ut_print_number(char *string, u64 number)
{
        char ascii_string[20];
        const char *pos1;
        char *pos2;

        pos1 = acpi_ut_put_number(ascii_string, number, 10, FALSE);
        pos2 = string;

        while (pos1 != ascii_string) {
                *(pos2++) = *(--pos1);
        }

        *pos2 = 0;
        return (string);
}

/*******************************************************************************
 *
 * FUNCTION:    acpi_ut_format_number
 *
 * PARAMETERS:  string              - String buffer with boundary
 *              end                 - Boundary of the string
 *              number              - The number to be converted
 *              base                - Base of the integer
 *              width               - Field width
 *              precision           - Precision of the integer
 *              type                - Special printing flags
 *
 * RETURN:      Updated position for next valid character
 *
 * DESCRIPTION: Print an integer into a string with any base and any precision.
 *
 ******************************************************************************/

static char *acpi_ut_format_number(char *string,
                                   char *end,
                                   u64 number,
                                   u8 base, s32 width, s32 precision, u8 type)
{
        char *pos;
        char sign;
        char zero;
        u8 need_prefix;
        u8 upper;
        s32 i;
        char reversed_string[66];

        /* Parameter validation */

        if (base < 2 || base > 16) {
                return (NULL);
        }

        if (type & ACPI_FORMAT_LEFT) {
                type &= ~ACPI_FORMAT_ZERO;
        }

        need_prefix = ((type & ACPI_FORMAT_PREFIX)
                       && base != 10) ? TRUE : FALSE;
        upper = (type & ACPI_FORMAT_UPPER) ? TRUE : FALSE;
        zero = (type & ACPI_FORMAT_ZERO) ? '0' : ' ';

        /* Calculate size according to sign and prefix */

        sign = '\0';
        if (type & ACPI_FORMAT_SIGN) {
                if ((s64)number < 0) {
                        sign = '-';
                        number = -(s64)number;
                        width--;
                } else if (type & ACPI_FORMAT_SIGN_PLUS) {
                        sign = '+';
                        width--;
                } else if (type & ACPI_FORMAT_SIGN_PLUS_SPACE) {
                        sign = ' ';
                        width--;
                }
        }
        if (need_prefix) {
                width--;
                if (base == 16) {
                        width--;
                }
        }

        /* Generate full string in reverse order */

        pos = acpi_ut_put_number(reversed_string, number, base, upper);
        i = (s32)ACPI_PTR_DIFF(pos, reversed_string);

        /* Printing 100 using %2d gives "100", not "00" */

        if (i > precision) {
                precision = i;
        }

        width -= precision;

        /* Output the string */

        if (!(type & (ACPI_FORMAT_ZERO | ACPI_FORMAT_LEFT))) {
                while (--width >= 0) {
                        string = acpi_ut_bound_string_output(string, end, ' ');
                }
        }
        if (sign) {
                string = acpi_ut_bound_string_output(string, end, sign);
        }
        if (need_prefix) {
                string = acpi_ut_bound_string_output(string, end, '0');
                if (base == 16) {
                        string =
                            acpi_ut_bound_string_output(string, end,
                                                        upper ? 'X' : 'x');
                }
        }
        if (!(type & ACPI_FORMAT_LEFT)) {
                while (--width >= 0) {
                        string = acpi_ut_bound_string_output(string, end, zero);
                }
        }

        while (i <= --precision) {
                string = acpi_ut_bound_string_output(string, end, '0');
        }
        while (--i >= 0) {
                string = acpi_ut_bound_string_output(string, end,
                                                     reversed_string[i]);
        }
        while (--width >= 0) {
                string = acpi_ut_bound_string_output(string, end, ' ');
        }

        return (string);
}

/*******************************************************************************
 *
 * FUNCTION:    vsnprintf
 *
 * PARAMETERS:  string              - String with boundary
 *              size                - Boundary of the string
 *              format              - Standard printf format
 *              args                - Argument list
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to a string using argument list pointer.
 *
 ******************************************************************************/

int vsnprintf(char *string, acpi_size size, const char *format, va_list args)
{
        u8 base;
        u8 type;
        s32 width;
        s32 precision;
        char qualifier;
        u64 number;
        char *pos;
        char *end;
        char c;
        const char *s;
        const void *p;
        s32 length;
        int i;

        pos = string;

        size = ACPI_MIN(size, ACPI_PTR_DIFF(ACPI_MAX_PTR, string));
        end = string + size;

        for (; *format; ++format) {
                if (*format != '%') {
                        pos = acpi_ut_bound_string_output(pos, end, *format);
                        continue;
                }

                type = 0;
                base = 10;

                /* Process sign */

                do {
                        ++format;
                        if (*format == '#') {
                                type |= ACPI_FORMAT_PREFIX;
                        } else if (*format == '0') {
                                type |= ACPI_FORMAT_ZERO;
                        } else if (*format == '+') {
                                type |= ACPI_FORMAT_SIGN_PLUS;
                        } else if (*format == ' ') {
                                type |= ACPI_FORMAT_SIGN_PLUS_SPACE;
                        } else if (*format == '-') {
                                type |= ACPI_FORMAT_LEFT;
                        } else {
                                break;
                        }

                } while (1);

                /* Process width */

                width = -1;
                if (isdigit((int)*format)) {
                        format = acpi_ut_scan_number(format, &number);
                        width = (s32)number;
                } else if (*format == '*') {
                        ++format;
                        width = va_arg(args, int);
                        if (width < 0) {
                                width = -width;
                                type |= ACPI_FORMAT_LEFT;
                        }
                }

                /* Process precision */

                precision = -1;
                if (*format == '.') {
                        ++format;
                        if (isdigit((int)*format)) {
                                format = acpi_ut_scan_number(format, &number);
                                precision = (s32)number;
                        } else if (*format == '*') {
                                ++format;
                                precision = va_arg(args, int);
                        }

                        if (precision < 0) {
                                precision = 0;
                        }
                }

                /* Process qualifier */

                qualifier = -1;
                if (*format == 'h' || *format == 'l' || *format == 'L') {
                        qualifier = *format;
                        ++format;

                        if (qualifier == 'l' && *format == 'l') {
                                qualifier = 'L';
                                ++format;
                        }
                }

                switch (*format) {
                case '%':

                        pos = acpi_ut_bound_string_output(pos, end, '%');
                        continue;

                case 'c':

                        if (!(type & ACPI_FORMAT_LEFT)) {
                                while (--width > 0) {
                                        pos =
                                            acpi_ut_bound_string_output(pos,
                                                                        end,
                                                                        ' ');
                                }
                        }

                        c = (char)va_arg(args, int);
                        pos = acpi_ut_bound_string_output(pos, end, c);

                        while (--width > 0) {
                                pos =
                                    acpi_ut_bound_string_output(pos, end, ' ');
                        }
                        continue;

                case 's':

                        s = va_arg(args, char *);
                        if (!s) {
                                s = "<NULL>";
                        }
                        length = (s32)acpi_ut_bound_string_length(s, precision);
                        if (!(type & ACPI_FORMAT_LEFT)) {
                                while (length < width--) {
                                        pos =
                                            acpi_ut_bound_string_output(pos,
                                                                        end,
                                                                        ' ');
                                }
                        }

                        for (i = 0; i < length; ++i) {
                                pos = acpi_ut_bound_string_output(pos, end, *s);
                                ++s;
                        }

                        while (length < width--) {
                                pos =
                                    acpi_ut_bound_string_output(pos, end, ' ');
                        }
                        continue;

                case 'o':

                        base = 8;
                        break;

                case 'X':

                        type |= ACPI_FORMAT_UPPER;
                        ACPI_FALLTHROUGH;

                case 'x':

                        base = 16;
                        break;

                case 'd':
                case 'i':

                        type |= ACPI_FORMAT_SIGN;

                case 'u':

                        break;

                case 'p':

                        if (width == -1) {
                                width = 2 * sizeof(void *);
                                type |= ACPI_FORMAT_ZERO;
                        }

                        p = va_arg(args, void *);
                        pos =
                            acpi_ut_format_number(pos, end, ACPI_TO_INTEGER(p),
                                                  16, width, precision, type);
                        continue;

                default:

                        pos = acpi_ut_bound_string_output(pos, end, '%');
                        if (*format) {
                                pos =
                                    acpi_ut_bound_string_output(pos, end,
                                                                *format);
                        } else {
                                --format;
                        }
                        continue;
                }

                if (qualifier == 'L') {
                        number = va_arg(args, u64);
                        if (type & ACPI_FORMAT_SIGN) {
                                number = (s64)number;
                        }
                } else if (qualifier == 'l') {
                        number = va_arg(args, unsigned long);
                        if (type & ACPI_FORMAT_SIGN) {
                                number = (s32)number;
                        }
                } else if (qualifier == 'h') {
                        number = (u16)va_arg(args, int);
                        if (type & ACPI_FORMAT_SIGN) {
                                number = (s16)number;
                        }
                } else {
                        number = va_arg(args, unsigned int);
                        if (type & ACPI_FORMAT_SIGN) {
                                number = (signed int)number;
                        }
                }

                pos = acpi_ut_format_number(pos, end, number, base,
                                            width, precision, type);
        }

        if (size > 0) {
                if (pos < end) {
                        *pos = '\0';
                } else {
                        end[-1] = '\0';
                }
        }

        return ((int)ACPI_PTR_DIFF(pos, string));
}

/*******************************************************************************
 *
 * FUNCTION:    snprintf
 *
 * PARAMETERS:  string              - String with boundary
 *              size                - Boundary of the string
 *              Format, ...         - Standard printf format
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to a string.
 *
 ******************************************************************************/

int snprintf(char *string, acpi_size size, const char *format, ...)
{
        va_list args;
        int length;

        va_start(args, format);
        length = vsnprintf(string, size, format, args);
        va_end(args);

        return (length);
}

/*******************************************************************************
 *
 * FUNCTION:    sprintf
 *
 * PARAMETERS:  string              - String with boundary
 *              Format, ...         - Standard printf format
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to a string.
 *
 ******************************************************************************/

int sprintf(char *string, const char *format, ...)
{
        va_list args;
        int length;

        va_start(args, format);
        length = vsnprintf(string, ACPI_UINT32_MAX, format, args);
        va_end(args);

        return (length);
}

#ifdef ACPI_APPLICATION
/*******************************************************************************
 *
 * FUNCTION:    vprintf
 *
 * PARAMETERS:  format              - Standard printf format
 *              args                - Argument list
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to stdout using argument list pointer.
 *
 ******************************************************************************/

int vprintf(const char *format, va_list args)
{
        acpi_cpu_flags flags;
        int length;

        flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
        length = vsnprintf(acpi_gbl_print_buffer,
                           sizeof(acpi_gbl_print_buffer), format, args);

        (void)fwrite(acpi_gbl_print_buffer, length, 1, ACPI_FILE_OUT);
        acpi_os_release_lock(acpi_gbl_print_lock, flags);

        return (length);
}

/*******************************************************************************
 *
 * FUNCTION:    printf
 *
 * PARAMETERS:  Format, ...         - Standard printf format
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to stdout.
 *
 ******************************************************************************/

int printf(const char *format, ...)
{
        va_list args;
        int length;

        va_start(args, format);
        length = vprintf(format, args);
        va_end(args);

        return (length);
}

/*******************************************************************************
 *
 * FUNCTION:    vfprintf
 *
 * PARAMETERS:  file                - File descriptor
 *              format              - Standard printf format
 *              args                - Argument list
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to a file using argument list pointer.
 *
 ******************************************************************************/

int vfprintf(FILE * file, const char *format, va_list args)
{
        acpi_cpu_flags flags;
        int length;

        flags = acpi_os_acquire_lock(acpi_gbl_print_lock);
        length = vsnprintf(acpi_gbl_print_buffer,
                           sizeof(acpi_gbl_print_buffer), format, args);

        (void)fwrite(acpi_gbl_print_buffer, length, 1, file);
        acpi_os_release_lock(acpi_gbl_print_lock, flags);

        return (length);
}

/*******************************************************************************
 *
 * FUNCTION:    fprintf
 *
 * PARAMETERS:  file                - File descriptor
 *              Format, ...         - Standard printf format
 *
 * RETURN:      Number of bytes actually written.
 *
 * DESCRIPTION: Formatted output to a file.
 *
 ******************************************************************************/

int fprintf(FILE * file, const char *format, ...)
{
        va_list args;
        int length;

        va_start(args, format);
        length = vfprintf(file, format, args);
        va_end(args);

        return (length);
}
#endif