These functions are intended to be a complete replacement for sprintf
and sprintf_irc. They are a (nearly) complete reimplementation, and
of course they're snprintf clones, making it more difficult for
accidental buffer overflows to crop up.
First off, what's missing? These functions support all ANSI C
conversion specifiers and selected ones from ISO 9x, with the
exception of all floating-point conversions. The floating-point
conversions are tricky, and will likely be dependent on the
representation of a floating-point number on a particular
architecture. While that representation is likely to conform to some
standard, it is not currently used in ircu, so seemed like a good
thing to omit, given the difficulty of implementing it.
There are two more things missing from this implementation that would
be required by ANSI; the first is support for multibyte character
strings, and the second is support for locales, neither of which have
any relevance for ircu, so again omission seemed to be a good policy.
Additionally, %#x always causes '0x' (or '0X') to be printed, even if
the number is zero.
These functions also have some extensions not seen in a
standards-compliant implementation; technically, the ISO 9x extensions
fall into this category, for instance. The ISO 9x extensions
supported are type extensions--%ju, %tu, and %zu, for instance; %qu
and %hhu are also supported. The extensions added for use in ircu are
%Tu, which takes a time_t, and the new %C conversion, which inserts
either a numeric or a nick, dependent on the <dest> parameter. The
GNU %m extension, which inserts the strerror() string corresponding to
the current value of errno, is also supported, as is a special %v
extension, which essentially does a recursive call to ircd_snprintf.
The following description is descended from the Linux man page for the
printf family of functions.
The format string is composed of zero or more directives: ordinary
characters (not %), which are copied unchanged to the output stream;
and conversion specifications, each of which results in fetching zero
or more subsequent arguments. Each conversion specification is
introduced by the character %. The arguments must correspond properly
(after type promotion) with the conversion specifier. After the %,
the following appear in sequence:
* Zero or more of the following flags:
# specifying that the value should be converted to an "alternate
form." For c, d, i, n, p, s, and u conversions, this option
has no effect. For o conversions, the precision of the number
is increased to force the first character of the output string
to a zero (except if a zero value is printed with an explicit
precision of zero). For x and X conversions, the string '0x'
(or '0X' for X conversions) is prepended to it. For e, E, f,
g, and G conversions, the result will always contain a decimal
point, even if no digits follow it (normally, a decimal point
appears in the results of those conversions only if a digit
follows). For g and G conversions, trailing zeros are not
removed from the result as they would otherwise be. For C
conversions, if the destination is local and the origin is a
user, the nick!user@host form is used.
0 specifying zero padding. For all conversions except n, the
converted value is padded on the left with zeros rather than
blanks. If a precision is given with a numeric conversion (d,
i, o, u, i, x, and X), the 0 flag is ignored.
- (a negative field width flag) indicates the converted value is
to be left adjusted on the field boundary. Except for n
conversions, the converted value is padded on the right with
blanks, rather than on the left with blanks or zeros. A -
overrides a 0 if both are given.
' ' (a space) specifying that a blank should be left before a
positive number produced by a signed conversion (d, e, E, f,
g, G, or i).
+ specifying that a sign always be placed before a number
produced by a signed conversion. A + overrides a space if
both are used.
: specifying that a struct Client name should be preceded by a
':' character if the destination is a user
* An optional decimal digit string specifying a minimum field width.
If the converted value has fewer characters than the field width, it
will be padded with spaces on the left (or right, if the
left-adjustment flag has been given) to fill out the field width.
* An optional precision, in the form of a period (`.') followed by an
optional digit string. If the digit string is omitted, the
precision is taken as zero. This gives the minimum number of digits
to appear for d, i, o, u, x, and X conversions, the number of digits
to appear after the decimal-point for e, E, and f conversions, the
maximum number of significant digits for g and G conversions, or the
maximum number of characters to be printed from a string for s
conversions.
* The optional character h, specifying that a following d, i, o, u, x,
or X conversion corresponds to a short int or unsigned short int
argument, or that a following n conversion corresponds to a pointer
to a short int argument. If the h character is given again, char is
used instead of short int.
* The optional character l (ell) specifying that a following d, i, o,
u, x, or X conversion applies to a pointer to a long int or unsigned
long int argument, or that a following n conversion corresponds to a
pointer to a long int argument.
* The character L specifying that a following e, E, f, g, or G
conversion corresponds to a long double argument, or a following d,
i, o, u, x, or X conversion corresponds to a long long argument.
Note that long long is not specified in ANSI C and therefore not
portable to all architectures.
* The optional character q. This is equivalent to L.
* A j character specifying that the following integer (d, i, o, u, x,
or X) conversion corresponds to an intmax_t argument.
* A t character specifying that the following integer (d, i, o, u, x,
or X) conversion corresponds to a ptrdiff_t argument.
* A z character specifying that the following integer (d, i, o, u, x,
or X) conversion corresponds to a size_t argument.
* A T character specifying that the following integer (d, i, o, u, x,
or X) conversion corresponds to a time_t argument.
* A character that specifies the type of conversion to be applied.
A field width or precision, or both, may be indicated by an asterisk
`*' instead of a digit string. In this case, an int argument supplies
the field width or precision. A negative field width is treated as a
left adjustment flag followed by a positive field width; a negative
precision is treated as though it were missing.
The conversion specifiers and their meanings are:
diouxX The int (or appropriate variant) argument is converted
to signed decimal (d and i), unsigned octal (o),
unsigned decimal (u), or unsigned hexadecimal (x and
X) notation. The letters abcdef are used for x
conversions; the letters ABCDEF are used for X
conversions. The precision, if any, gives the minimum
number of digits that must appear; if the converted
value requires fewer digits, it is padded on the left
with zeros.
eE [NOT IMPLEMENTED] The double argument is rounded and
converted in the style [-]d.dddedd where there is one
digit before the decimal-point character and the
number of digits after it is equal to the precision;
if the precision is missing, it is taken as 6; if the
precision is zero, no decimal-point character
appears. An E conversion uses the letter E (rather
than e) to introduce the exponent. The exponent
always contains at least two digits; if the value is
zero, the exponent is 00.
f [NOT IMPLEMENTED] The double argument is rounded and
converted to decimal notation in the style
[-]ddd.ddd, where the number of digits after the
decimal-point character is equal to the precision
specification. If the precision is missing, it is
taken as 6; if the precision is explicitly zero, no
decimal-point character appears. If a decimal point
appears, at least one digit appears before it.
g [NOT IMPLEMENTED] The double argument is converted in
style f or e (or E for G conversions). The precision
specifies the number of significant digits. If the
precision is missing, 6 digits are given; if the
precision is zero, it is treated as 1. Style e is
used if the exponent from its conversion is less than
-4 or greater than or equal to the precision.
Trailing zeros are removed from the fractional part of
the result; a decimal point appears only if it is
followed by at least one digit.
c The int argument is converted to an unsigned char, and
the resulting character is written.
s The "char *" argument is expected to be a pointer to
an array of character type (pointer to a string).
Characters from the array are written up to (but not
including) a terminating NUL character; if a precision
is specified, no more than the number specified are
written. If a precision is given, no null character
need be present; if the precision is not specified, or
is greater than the size of the array, the array must
contain a terminating NUL character.
p The "void *" pointer argument is printed in
hexadecimal (as if by %#x or %#lx).
n The number of characters written so far is stored into
the integer indicated by the ``int *'' (or variant)
pointer argument. No argument is converted.
m The error message associated with the current value of
errno is printed as if by %s.
C The client argument identifier is printed under the
control of the <dest> argument; if <dest> is NULL or
is a user, the client's name (nickname or server name)
is printed; otherwise, the client's network numeric is
printed.
H The channel argument identifier (channel name) is
printed.
v The argument given must be a pointer to a struct
VarData with vd_format and vd_args must be initialized
appropriately. On return, vd_chars will contain the
number of characters added to the buffer, and
vd_overflow will contain the number of characters that
could not be added due to buffer overflow or due to a
precision.
% A `%' is written. No argument is converted. The
complete conversion specification is `%%'.
In no case does a non-existent or small field width cause truncation
of a field; if the result of a conversion is wider than the field
width, the field is expanded to contain the conversion result.
<struct>
struct VarData {
size_t vd_chars; /* number of characters inserted */
size_t vd_overflow; /* number of characters that couldn't be */
const char *vd_format; /* format string */
va_list vd_args; /* arguments for %v */
};
This structure is used by the %v conversion specification. The
_vd_format_ element must contain a format string, and the _vd_args_
element must be a variable argument list. Upon return from
ircd_snprintf() or ircd_vsnprintf(), the _vd_chars_ element will
contain the number of characters that were able to be inserted, and
the _vd_overflow_ element will contain the number of characters that
could not be inserted.
</struct>
<function>
int ircd_snprintf(struct Client *dest, char *buf, size_t buf_len,
const char *format, ...);
This formats the argument list, under control of the _format_, into
the buffer specified by _buf_, the size of which is specified by
_buf_len_. The _dest_ parameter is used to determine whether to use a
numeric or a nickname for %C conversions.
</function>
<function>
int ircd_vsnprintf(struct Client *dest, char *buf, size_t buf_len,
const char *format, va_list args);
This function is identical to the ircd_snprintf() function except for
the variable argument list given by _args_.
</function>
<authors>
Kev <klmitch@mit.edu>
</authors>
<changelog>
[2001-6-15 Kev] Initial documentation of the ircd_snprintf family of
functions.
</changelog>