diff options
Diffstat (limited to 'lib/qfstring.c')
-rw-r--r-- | lib/qfstring.c | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/lib/qfstring.c b/lib/qfstring.c new file mode 100644 index 00000000..30ee441c --- /dev/null +++ b/lib/qfstring.c @@ -0,0 +1,1066 @@ +/* Some string handling + * Copyright (C) 2010 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <stdbool.h> +#include <stdint.h> + +#include "qfstring.h" + +/*============================================================================== + */ + +/*------------------------------------------------------------------------------ + * Initialise qf_str -- to given size (which includes the '\n') + * + * Sets pointers and terminates an empty string with one byte reserved for the + * terminating '\n'. + * + * This operation is async-signal-safe. + */ +extern void +qfs_init(qf_str qfs, char* str, size_t size) +{ + assert(size > 0) ; + + qfs->str = qfs->ptr = str ; + qfs->end = qfs->str + size - 1 ; + + *str = '\0' ; +} ; + +/*------------------------------------------------------------------------------ + * Terminate string with the given string. + * + * If necessary, characters are discarded from the end of the string in order + * to fit in the terminating stuff. + * + * If the terminating stuff won't fit, as much of the end if the terminating + * stuff as possible is copied to the string -- displacing any existing + * contents. + * + * This operation is async-signal-safe. + */ +extern void +qfs_term(qf_str qfs, const char* src) +{ + int len ; + int excess ; + + if ((src == NULL) || (*src == '\0')) + { + *qfs->ptr = '\0' ; /* should be true anyway */ + return ; + } ; + + len = strlen(src) ; + excess = qfs_len(qfs) - len ; + if (excess > 0) + { + if (excess <= (qfs->ptr - qfs->str)) + qfs->ptr -= excess ; + else + { + int want = len ; + len = qfs->end - qfs->str ; /* take what can... */ + src += (want - len) ; /* ... from the end */ + qfs->ptr = qfs->str ; + } ; + } ; + + memcpy(qfs->ptr, src, len + 1) ; /* include the '\0' */ + qfs->ptr += len ; +} ; + +/*============================================================================== + * Appending to the string + */ + +/*------------------------------------------------------------------------------ + * Append as much as possible of the source string to the given qf_str. + * + * May append nothing at all ! + * + * This operation is async-signal-safe. + */ +extern void +qfs_append(qf_str qfs, const char* src) +{ + int n ; + + if ((src == NULL) || (*src == '\0')) + return ; + + n = strlen(src) ; + + if (n > qfs_left(qfs)) + n = qfs_left(qfs) ; + + if (n == 0) + return ; + + memcpy(qfs->ptr, src, n + 1) ; + qfs->ptr += n ; +} ; + +/*------------------------------------------------------------------------------ + * Append as much as possible of the first 'n' bytes of the source string to + * the given qf_str. + * + * May append nothing at all ! + * + * This operation is async-signal-safe. + */ +extern void +qfs_append_n(qf_str qfs, const char* src, size_t n) +{ + if ((int)n > qfs_left(qfs)) + n = qfs_left(qfs) ; + + if (n <= 0) + return ; + + memcpy(qfs->ptr, src, n) ; + qfs->ptr += n ; + + *qfs->ptr = '\0' ; +} ; + +/*------------------------------------------------------------------------------ + * Append upto 'n' copies of the given character to the qf_str + * + * May append nothing at all ! + * + * This operation is async-signal-safe. + */ +extern void +qfs_append_ch_x_n(qf_str qfs, char ch, size_t n) +{ + if ((int)n > qfs_left(qfs)) + n = qfs_left(qfs) ; + + if (n <= 0) + return ; + + while (n--) + *qfs->ptr++ = ch ; + + *qfs->ptr = '\0' ; +} ; + +/*------------------------------------------------------------------------------ + * Append as much as possible of the source string to the given qf_str, left or + * right justified to the given width. + * + * Ignores the width if the string is longer than it. + * + * Negative width => left justify. + * + * May append nothing at all ! + * + * This operation is async-signal-safe. + */ +extern void +qfs_append_justified(qf_str qfs, const char* src, int width) +{ + size_t n ; + + if ((src == NULL) || (*src == '\0')) + n = 0 ; + else + n = strlen(src) ; + + qfs_append_justified_n(qfs, src, n, width) ; +} ; + +/*------------------------------------------------------------------------------ + * Append as much as possible of the first 'n' bytes of the source string to + * the given qf_str, left or right justified to the given width. + * + * Ignores the width if the string is longer than it. + * + * Negative width => left justify. + * + * May append nothing at all ! + * + * This operation is async-signal-safe. + */ +extern void +qfs_append_justified_n(qf_str qfs, const char* src, size_t n, int width) +{ + if (n >= abs(width)) + width = 0 ; + + if (width > 0) + qfs_append_ch_x_n(qfs, ' ', width - n) ; + + qfs_append_n(qfs, src, n) ; + + if (width < 0) + qfs_append_ch_x_n(qfs, ' ', - width - n) ; +} ; + +/*============================================================================== + * Number conversion + */ + +static void +qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, + int width, int precision) ; + +/*------------------------------------------------------------------------------ + * Signed integer -- converted as per flags, width and precision. + * + * Result is appended to the given qf_str. + * + * This operation is async-signal-safe. + */ +extern void +qfs_signed(qf_str qfs, intmax_t s_val, enum pf_flags flags, + int width, int precision) +{ + uintmax_t u_val ; + int sign ; + + if (s_val < 0) + { + sign = -1 ; + u_val = (uintmax_t)(-(s_val + 1)) + 1 ; + } + else + { + sign = +1 ; + u_val = s_val ; + } ; + + qfs_number(qfs, u_val, sign, flags & ~pf_unsigned, width, precision) ; +} ; + +/*------------------------------------------------------------------------------ + * Unsigned integer -- converted as per flags, width and precision. + * + * Result is appended to the given qf_str. + * + * This operation is async-signal-safe. + */ +extern void +qfs_unsigned(qf_str qfs, uintmax_t u_val, enum pf_flags flags, + int width, int precision) +{ + qfs_number(qfs, u_val, 0, flags | pf_unsigned, width, precision) ; +} ; + +/*------------------------------------------------------------------------------ + * Address -- converted as per flags, width and precision. + * + * Result is appended to the given qf_str. + * + * This operation is async-signal-safe. + */ +extern void +qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, + int width, int precision) +{ + confirm(sizeof(uintmax_t) >= sizeof(uintptr_t)) ; + qfs_number(qfs, (uintptr_t)p_val, 0, flags | pf_unsigned, width, precision) ; +} ; + +/*------------------------------------------------------------------------------ + * Number conversion function. + * + * All number conversion ends up here. + * + * Accepts: pf_commas -- format with commas + * pf_plus -- requires '+' or '-' + * pf_space -- requires space or '-' + * pf_zeros -- zero fill to width + * pf_alt -- add '0x' or '0X' if hex (no effect on decimal) + * + * pf_precision -- explicit precision (needed if precision == 0) + * + * pf_hex -- render in hex + * pf_uc -- render in upper case + * + * pf_unsigned -- value is unsigned + * pf_ptr -- value is a void* pointer + * + * NB: pf_hex does NOT imply pf_unsigned. + * pf_uc does NOT imply pf_hex + * + * If the width is < 0 -- left justify in abs(width) -- zero fill ignored + * == 0 -- no width -- zero fill ignored + * > 0 -- right justify in width -- zero filling if req. + * + * If the precision is < 0 it is ignored (unless pf_hex, see below). + * + * If the precision is 0 it is ignored unless bf_precision is set. + * + * Precedence issues: + * + * * precision comes first. Disables zero fill. + * + * * commas come before zero fill. + * + * * signs and prefixes come before zero fill + * + * * pf_plus takes precedence over pf_space + * + * * pf_unsigned or sign == 0 takes precedence over pf_plus and pf_space. + * + * For hex output, pf_commas groups digits in 4's, separated by '_'. + * + * For hex output if precision is: + * + * -1 set precision to multiple of 2, just long enough for the value + * -2 set precision to multiple of 4, just long enough for the value + * + * (under all other conditions, -ve precision is ignored). + * + * Note: if the precision is explicitly 0, and the value is 0, and no other + * characters are to be generated -- ie no: pf_plus, pf_space, pf_zeros, + * or pf_alt (with pf_hex) -- then nothing is generated. + * + * This operation is async-signal-safe. + */ +static void +qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, + int width, int precision) +{ + enum + { + max_bits = 256, /* size of number can convert */ + max_digits = 90, /* could do octal ! */ + buf_size = 128, /* buffer to use for that */ + } ; + + confirm((sizeof(uintmax_t) * 8) <= max_bits) ; /* check max_bits */ + confirm((max_digits * 3) >= max_bits) ; /* check max_digits */ + + /* Buffer requires space for sign, '0x', digits, '00', commas, '\0' + * + * The '00' is for zero fill will commas, and is enough to extend the + * number to "000,...." -- that is, a full leading triple. + */ + confirm(buf_size > (1 + 2 + max_digits + (2 + (max_digits / 3)) + 1)) ; + + /* For hex commas the sum is similar, but smaller. */ + confirm((3 + (max_digits / 4)) < (2 + (max_digits / 3))) ; + + unsigned base ; + const char* digits ; + const char* radix_str ; + const char* sign_str ; + char num[buf_size] ; + char* p ; + char* e ; + int len ; + int radix_len ; + int sign_len ; + uintmax_t v ; + + char comma ; + int interval ; + + int zeros ; + + static const char lc[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'a', 'b', 'c', 'd', 'e', 'f' } ; + static const char uc[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F' } ; + + /* Tidy up the options */ + if (precision < 0) + { + if ((flags & pf_hex) && (precision >= -2)) + { + /* special precision for hex output */ + int unit = (precision == -1) ? 2 : 4 ; + v = val | 1 ; + precision = 0 ; + while (v != 0) + { + precision += unit ; + v >>= (unit * 4) ; + } ; + } + else + { + /* mostly, -ve precision is ignored */ + precision = 0 ; + flags &= ~pf_precision ; /* ignore precision < 0 */ + } ; + } ; + + if (precision > 0) + flags |= pf_precision ; /* act on precision > 0 */ + + if ((flags & pf_precision) || (width <= 0)) + flags &= ~pf_zeros ; /* turn off zero fill */ + + /* Set up any required sign and radix prefix */ + if ((flags & pf_unsigned) || (sign == 0)) + sign_str = "" ; + else if (sign < 0) + sign_str = "-" ; + else if (flags & pf_plus) + sign_str = "+" ; + else if (flags & pf_space) + sign_str = " " ; + else + sign_str = "" ; + + sign_len = strlen(sign_str) ; + + radix_str = "" ; + if ((flags & (pf_hex | pf_alt)) == (pf_hex | pf_alt)) + radix_str = (flags & pf_uc) ? "0X" : "0x" ; + + radix_len = strlen(radix_str) ; + + /* Turn off zero fill if left justify (width < 0) */ + if (width < 0) + flags &= ~pf_zeros ; + + /* Special case of explicit zero precision and value == 0 */ + if ((flags & pf_precision) && (precision == 0) && (val == 0)) + { + if (((flags & pf_zeros) == 0) && (sign_len == 0) && (radix_len == 0)) + { + qfs_append_justified_n(qfs, NULL, 0, width) ; + return ; + } ; + } ; + + /* Start with the basic digit conversion. */ + base = (flags & pf_hex) ? 16 : 10 ; + digits = (flags & pf_uc) ? uc : lc ; + + e = p = num + sizeof(num) - 1 ; + *p = '\0' ; + v = val ; + do + { + *--p = digits[v % base] ; + v /= base ; + } while ((v > 0) && (p > num)) ; + + assert(v == 0) ; + + len = e - p ; + + /* Worry about the precision */ + while ((precision > len) && (len < max_digits)) + { + *--p = '0' ; + ++len ; + } ; + + /* Worry about commas */ + comma = (flags & pf_hex) ? '_' : ',' ; + interval = (flags & pf_hex) ? 4 : 3 ; + + if (flags & pf_commas) + { + int c ; + int t ; + char* cq ; + char* cp ; + + c = (len - 1) / interval ; /* number of commas to insert */ + t = len % interval ; /* digits before first comma */ + if (t == 0) + t = interval ; + + len += c ; /* account for the commas */ + + cq = p ; + p -= c ; + cp = p ; + + assert(p > num) ; + + while (c--) + { + while (t--) + *cp++ = *cq++ ; + *cp++ = comma ; + } ; + + assert(len == (e - p)) ; + + /* commas and zero fill interact. Here fill the leading group. */ + zeros = width - (sign_len + radix_len + len) ; + if ((flags & pf_zeros) && (zeros > 0)) + { + int group_fill = interval - (len % (interval + 1)) ; + assert(group_fill < interval) ; + if (group_fill > zeros) + group_fill = zeros ; + + len += group_fill ; + while (group_fill--) + { + assert(p > num) ; + *--p = '0' ; + } ; + } ; + } ; + + assert(len == (e - p)) ; + + /* See if still need to worry about zero fill */ + zeros = width - (sign_len + radix_len + len) ; + if ((flags & pf_zeros) && (zeros > 0)) + { + /* Need to insert zeros and possible commas between sign and radix + * and the start of the number. + * + * Note that for commas the number has been arranged to have a full + * leading group. + * + * The width can be large... so do this by appending any sign and + * radix to the qf_str, and then the required leading zeros (with or + * without commas). + */ + if (sign_len != 0) + qfs_append_n(qfs, sign_str, sign_len) ; + + if (radix_len != 0) + qfs_append_n(qfs, radix_str, radix_len) ; + + if (flags & pf_commas) + { + /* Leading zeros with commas ! + * + * Start with ',', '0,', '00,' etc to complete the first group. + * Thereafter add complete groups. + */ + int g ; + int r ; + g = (zeros + interval - 1) / (interval + 1) ; + r = (zeros - 1) % (interval + 1) ; + + if (r == 0) + { + qfs_append_ch_x_n(qfs, comma, 1) ; + r = interval ; + } + + while (g--) + { + qfs_append_ch_x_n(qfs, '0', r) ; + qfs_append_ch_x_n(qfs, comma, 1) ; + r = interval ; + } ; + } + else + qfs_append_ch_x_n(qfs, '0', zeros) ; + + width = 0 ; /* have dealt with the width. */ + } + else + { + /* No leading zeros, so complete the number by adding any sign + * and radix. + */ + char* cp ; + + p -= sign_len + radix_len ; + len += sign_len + radix_len ; + assert(p >= num) ; + + cp = p ; + while (sign_len--) + *cp++ = *sign_str++ ; + while (radix_len--) + *cp++ = *radix_str++ ; + } ; + + /* Finally, can append the number -- respecting any remaining width */ + assert(len == (e - p)) ; + + qfs_append_justified_n(qfs, p, len, width) ; +} ; + +/*============================================================================== + * printf() and vprintf() type functions + */ + +enum pf_phase +{ + pfp_null, /* in ascending order */ + pfp_flags, + pfp_width, + pfp_precision, + pfp_num_type, + + pfp_done, + pfp_failed +} ; + +/* Number types for printing */ +enum arg_num_type +{ + ant_char, /* hh */ + ant_short, /* h */ + ant_int, /* default */ + ant_long, /* l */ + ant_long_long, /* ll */ + ant_intmax_t, /* j */ + ant_size_t, /* z */ + ant_ptr_t, /* void* */ + + ant_default = ant_int, +}; + +static enum pf_phase qfs_arg_string(qf_str qfs, va_list args, + enum pf_flags flags, int width, int precision) ; +static enum pf_phase qfs_arg_char(qf_str qfs, va_list args, + enum pf_flags flags, int width, int precision) ; +static enum pf_phase qfs_arg_number(qf_str qfs, va_list args, + enum pf_flags flags, int width, int precision, enum arg_num_type ant) ; + +/*------------------------------------------------------------------------------ + * Formatted print to qf_str -- cf printf() + * + * This operation is async-signal-safe. + */ +extern void +qfs_printf(qf_str qfs, const char* format, ...) +{ + va_list args; + + va_start (args, format); + qfs_vprintf(qfs, format, args); + va_end (args); +} ; + +/*------------------------------------------------------------------------------ + * Formatted print to qf_str -- cf vprintf() + * + * This operation is async-signal-safe. + */ +extern void +qfs_vprintf(qf_str qfs, const char *format, va_list args) +{ + if (format == NULL) + return ; + + while ((qfs->ptr < qfs->end) && (*format != '\0')) + { + /* Have space for one byte and current format byte is not '\0' */ + if (*format != '%') + *qfs->ptr++ = *format++ ; + else + { + const char* start = format++ ; /* start points at the '%' ... + ... step past it now */ + bool star = false ; + bool digit = false ; + int d = 0 ; + int width_sign = +1 ; + int width = 0 ; + int precision = 0 ; + enum arg_num_type ant = ant_default ; + enum pf_flags flags = pf_none ; + enum pf_phase phase = pfp_null ; + + while (phase < pfp_done) + { + switch (*format++) /* get next and step past it */ + { + case '%': /* %% only */ + if (phase == pfp_null) + *qfs->ptr++ = '%' ; + phase = (phase == pfp_null) ? pfp_done : pfp_failed ; + break ; + + case '\'': + flags |= pf_commas ; + phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; + break ; + + case '-': + width_sign = -1 ; + phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; + break ; + + case '+': + flags |= pf_plus ; + phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; + break ; + + case ' ': + flags |= pf_space ; + phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; + break ; + + case '0': + if (phase <= pfp_flags) + { + flags |= pf_zeros ; + phase = pfp_flags ; + break ; + } ; + /* fall through */ + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + d = *(format - 1) - '0' ; + if (!star && (phase <= pfp_width)) + { + phase = pfp_width ; + width = (width * 10) + (d * width_sign) ; + } + else if (!star && (phase == pfp_precision)) + precision = (precision * 10) + d ; + else + phase = pfp_failed ; + + digit = true ; + break ; + + case '*': + if (!star && !digit && (phase <= pfp_width)) + { + phase = pfp_width ; + width = va_arg(args, int) ; + } + else if (!star && !digit && (phase == pfp_precision)) + { + precision = va_arg(args, int) ; + if (precision < 0) + { + precision = 0 ; + flags &= ~pf_precision ; + } ; + } + else + phase = pfp_failed ; + + star = true ; + break ; + + case '.': + phase = (phase <= pfp_precision) ? pfp_precision : pfp_failed; + flags |= pf_precision ; + precision = 0 ; + break ; + + case 'l': /* 1 or 2 'l', not 'h', 'j' or 'z' */ + phase = pfp_num_type ; + if (ant == ant_default) + ant = ant_long ; + else if (ant == ant_long) + ant = ant_long_long ; + else + phase = pfp_failed ; + break ; + + case 'h': /* 1 or 2 'h', not 'l', 'j' or 'z' */ + phase = pfp_num_type ; + if (ant == ant_default) + ant = ant_short ; + else if (ant == ant_short) + ant = ant_char ; + else + phase = pfp_failed ; + break ; + + case 'j': /* 1 'j', not 'h', 'l' or 'z' */ + phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ; + ant = ant_intmax_t ; + break ; + + case 'z': /* 1 'z', not 'h', 'l' or 'j' */ + phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ; + ant = ant_size_t ; + break ; + + case 's': + if (phase == pfp_num_type) + phase = pfp_failed ; /* don't do 'l' etc. */ + else + phase = qfs_arg_string(qfs, args, flags, width, precision) ; + break ; + + case 'c': + if (phase == pfp_num_type) + phase = pfp_failed ; /* don't do 'l' etc. */ + else + phase = qfs_arg_char(qfs, args, flags, width, precision) ; + break ; + + case 'd': + case 'i': + phase = qfs_arg_number(qfs, args, flags, width, precision, + ant) ; + break ; + + case 'u': + phase = qfs_arg_number(qfs, args, flags | pf_unsigned, width, + precision, ant) ; + break ; + + case 'x': + phase = qfs_arg_number(qfs, args, flags | pf_hex_x, width, + precision, ant) ; + break ; + + case 'X': + phase = qfs_arg_number(qfs, args, flags | pf_hex_X, width, + precision, ant) ; + break ; + + case 'p': + if (phase == pfp_num_type) + phase = pfp_failed ; + else + phase = qfs_arg_number(qfs, args, flags | pf_void_p, width, + precision, ant_ptr_t) ; + break ; + + default: /* unrecognised format */ + phase = pfp_failed ; + break ; + } ; + } ; + + if (phase == pfp_failed) + { + format = start ; /* back to the start */ + *qfs->ptr++ = *format++ ; /* copy the '%' */ + } ; + } ; + } ; + + *qfs->ptr = '\0' ; +} ; + +/*------------------------------------------------------------------------------ + * %s handler + * + * Accepts: width + * precision + * pf_precision -- explicit precision + * + * Rejects: pf_commas -- "'" seen + * pf_plus -- "+" seen + * pf_space -- " " seen + * pf_zeros -- "0" seen + * pf_alt -- "#" seen + * + * Won't get: pf_hex + * pf_uc + * pf_unsigned + * pf_ptr + * + * This operation is async-signal-safe. + */ +static enum pf_phase +qfs_arg_string(qf_str qfs, va_list args, enum pf_flags flags, + int width, int precision) +{ + const char* src ; + int len ; + + src = va_arg(args, char*) ; + + if (flags != (flags & pf_precision)) + return pfp_failed ; + + len = strlen(src) ; + if (((precision > 0) || (flags & pf_precision)) && (len > precision)) + len = precision ; + + qfs_append_justified_n(qfs, src, len, width) ; + + return pfp_done ; +} ; + +/*------------------------------------------------------------------------------ + * %c handler + * + * Accepts: width + * + * Rejects: precision + * pf_precision -- explicit precision + * pf_commas -- "'" seen + * pf_plus -- "+" seen + * pf_space -- " " seen + * pf_zeros -- "0" seen + * pf_alt -- "#" seen + * + * Won't get: pf_hex + * pf_uc + * pf_unsigned + * pf_ptr + * + * This operation is async-signal-safe. + */ +static enum pf_phase +qfs_arg_char(qf_str qfs, va_list args, enum pf_flags flags, + int width, int precision) +{ + unsigned char ch ; + + ch = va_arg(args, int) ; + + if ((flags != 0) || (precision != 0)) + return pfp_failed ; + + qfs_append_justified_n(qfs, (char*)&ch, 1, width) ; + + return pfp_done ; +} ; + +/*------------------------------------------------------------------------------ + * %d, %i, %u, %x, %X and %p handler + * + * Accepts: pf_commas -- format with commas + * pf_minus -- left justify (any width will be -ve) + * pf_plus -- requires sign + * pf_space -- requires space or '-' + * pf_zeros -- zero fill to width + * pf_alt -- '0x' or '0X' for hex + * + * pf_precision -- precision specified + * + * pf_unsigned -- value is unsigned + * pf_ptr -- value is a void* pointer + * pf_hex -- render in hex + * pf_uc -- render hex in upper case + * + * and: all the number argument types. + * + * This operation is async-signal-safe. + */ +static enum pf_phase +qfs_arg_number(qf_str qfs, va_list args, enum pf_flags flags, + int width, int precision, enum arg_num_type ant) +{ + uintmax_t u_val ; + intmax_t s_val ; + + /* Special for hex with '0... if no explicit precision, set -1 for byte + * and -2 for everything else -- see qfs_number(). + */ + if (((flags & pf_precision) == 0) && (flags & pf_hex)) + { + if ((flags & (pf_commas | pf_zeros)) == (pf_commas | pf_zeros)) + { + precision = (ant == ant_char) ? -1 : -2 ; + flags |= pf_precision ; + } ; + } ; + + /* It is assumed that all values can be mapped to a uintmax_t */ + confirm(sizeof(uintmax_t) >= sizeof(uintptr_t)) ; + + if (flags & pf_unsigned) + { + switch (ant) + { + case ant_char: + case ant_short: + u_val = va_arg(args, int) ; + break ; + + case ant_int: + u_val = va_arg(args, unsigned int) ; + break ; + + case ant_long: + u_val = va_arg(args, unsigned long) ; + break ; + + case ant_long_long: + u_val = va_arg(args, unsigned long long) ; + break ; + + case ant_intmax_t: + u_val = va_arg(args, uintmax_t) ; + break ; + + case ant_size_t: + u_val = va_arg(args, size_t) ; + break ; + + case ant_ptr_t: + u_val = va_arg(args, uintptr_t) ; + break ; + + default: + zabort("impossible integer size") ; + } ; + + qfs_unsigned(qfs, u_val, flags, width, precision) ; + } + else + { + switch (ant) + { + case ant_char: + case ant_short: + s_val = va_arg(args, int) ; + break ; + + case ant_int: + s_val = va_arg(args, signed int) ; + break ; + + case ant_long: + s_val = va_arg(args, signed long) ; + break ; + + case ant_long_long: + s_val = va_arg(args, signed long long) ; + break ; + + case ant_intmax_t: + s_val = va_arg(args, intmax_t) ; + break ; + + case ant_size_t: + s_val = va_arg(args, ssize_t) ; + break ; + + case ant_ptr_t: + s_val = va_arg(args, intptr_t) ; + break ; + + default: + zabort("impossible integer size") ; + } ; + + qfs_signed(qfs, s_val, flags, width, precision) ; + } ; + + /* construct a digit string, the hard way */ + + return pfp_done ; +} ; + |