ircu api - ircd snprintf

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



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 decimal digit string specifying a minimum field width.

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.
 * An optional precision, in the form of a period (`.') followed by an

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 h, specifying that a following d, i, o, u, x,

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 optional character l (ell) specifying that a following d, i, o,

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 character L specifying that a following e, E, f, g, or G


 * The optional character q. This is equivalent to L.

or X) conversion corresponds to an intmax_t argument.
 * A j character specifying that the following integer (d, i, o, u, x,

or X) conversion corresponds to a ptrdiff_t argument.
 * A t character specifying that the following integer (d, i, o, u, x,

or X) conversion corresponds to a size_t argument.
 * A z character specifying that the following integer (d, i, o, u, x,

or X) conversion corresponds to a time_t argument.
 * A T character specifying that the following integer (d, i, o, u, x,


 * 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 argument; if 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 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.

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.

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_.

Kev 

[2001-6-15 Kev] Initial documentation of the ircd_snprintf family of functions.