| // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
| /****************************************************************************** |
| * |
| * Module Name: utprint - Formatted printing routines |
| * |
| * Copyright (C) 2000 - 2023, 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; |
| |
| if (size != ACPI_UINT32_MAX) { |
| end = string + size; |
| } else { |
| end = ACPI_CAST_PTR(char, ACPI_UINT32_MAX); |
| } |
| |
| 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 |