// 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