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