X-Authentication-Warning: delorie.com: mail set sender to djgpp-workers-bounces using -f X-Recipient: djgpp-workers AT delorie DOT com X-Authenticated: #27081556 X-Provags-ID: V01U2FsdGVkX19tTwhLwfNt66RLmPVIHkzCqcwlOzvBMjZastWOgL fJiAurxqO5UvEi From: Juan Manuel Guerrero To: djgpp-workers AT delorie DOT com Subject: Re: Implementation of certain conversion specifiers in _doprnt (patch #4) Date: Thu, 24 Apr 2008 16:32:26 +0200 User-Agent: KMail/1.9.5 MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200804241632.27815.juan.guerrero@gmx.de> X-Y-GMX-Trusted: 0 Reply-To: djgpp-workers AT delorie DOT com Implements numeric conversion specifier and provides test programs. Regards, Juan M. Guerrero 2008-04-24 Juan Manuel Guerrero Diffs against djgpp CVS head of 2008-04-22. * src/libc/ansi/stdio/doprnt.c: Implementation of numeric conversion specifier support. New function __traverse_argument_list added. * src/libc/ansi/stdio/printf.txh: Description of numeric conversion specifier for printf added. * src/docs/kb/wc204.txi: Info about numeric conversion specifier support for doprnt.c and printf family of functions added. *tests/libc/ansi/stdio/makefile: Testcase file printf4.c for testing numeric conversion specifiers added. * tests/libc/ansi/stdio/printf4.c: New file. Implements tests for numeric conversion specifiers. * tests/libc/ansi/stdio/printf5.c: New file. Implements tests for flags, conversion modifiers and numeric conversion specifiers for the printf family of functions. diff -aprNU3 djgpp.orig/src/docs/kb/wc204.txi djgpp/src/docs/kb/wc204.txi --- djgpp.orig/src/docs/kb/wc204.txi 2008-04-24 15:09:08 +0000 +++ djgpp/src/docs/kb/wc204.txi 2008-04-24 15:13:20 +0000 @@ -1137,3 +1137,9 @@ of @code{Unnormal}. The @code{a}, @code{A} and @code{F} conversion specifiers are now supported by @code{_doprnt} and the @code{printf} family of functions. + +@findex _doprnt AT r{, and numeric conversion specifiers} +@findex printf AT r{, and numeric conversion specifiers} +The @code{%n$} and @code{*m$} numeric conversion specifiers +are now supported by @code{_doprnt} and the @code{printf} +family of functions. diff -aprNU3 djgpp.orig/src/libc/ansi/stdio/doprnt.c djgpp/src/libc/ansi/stdio/doprnt.c --- djgpp.orig/src/libc/ansi/stdio/doprnt.c 2008-04-24 15:09:08 +0000 +++ djgpp/src/libc/ansi/stdio/doprnt.c 2008-04-24 15:13:20 +0000 @@ -1,3 +1,4 @@ +/* Copyright (C) 2008 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2003 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2002 DJ Delorie, see COPYING.DJ for details */ /* Copyright (C) 2001 DJ Delorie, see COPYING.DJ for details */ @@ -44,12 +45,12 @@ static char *grouping; #define PUTC(ch) (void) putc(ch, fp) -#define ARG(basetype) _ulonglong = \ - flags&LONGDBL ? va_arg(argp, long long basetype) : \ - flags&LONGINT ? va_arg(argp, long basetype) : \ - flags&SHORTINT ? (short basetype)va_arg(argp, int) : \ - flags&CHARINT ? (char basetype)va_arg(argp, int) : \ - (basetype)va_arg(argp, int) +#define ARG(basetype) _ulonglong = \ + flags & LONGDBL ? va_arg(argp, long long basetype) : \ + flags & LONGINT ? va_arg(argp, long basetype) : \ + flags & SHORTINT ? (short basetype)va_arg(argp, int) : \ + flags & CHARINT ? (char basetype)va_arg(argp, int) : \ + (basetype)va_arg(argp, int) #define CONVERT(type, value, base, string, case) \ do { \ @@ -65,20 +66,20 @@ static char *grouping; || ((x).ldt.exponent == 0x0000U && !((x).ldt.mantissah & 0x80000000UL))) #define IS_ZERO(x) ((x).ldt.exponent == 0x0U && (x).ldt.mantissah == 0x0UL && (x).ldt.mantissal == 0x0UL) #define IS_NAN(x) ((x).ldt.exponent == 0x7FFFU && ((x).ldt.mantissah & 0x7FFFFFFFUL || (x).ldt.mantissal)) -#define IS_PSEUDO_NUMBER(x) (((x).ldt.exponent != 0x0000U && !((x).ldt.mantissah & 0x80000000UL)) /* Pseudo-NaN, Pseudo-Infinity and Unnormal. */ \ +#define IS_PSEUDO_NUMBER(x) (((x).ldt.exponent != 0x0000U && !((x).ldt.mantissah & 0x80000000UL)) /* Pseudo-NaN, Pseudo-Infinity and Unnormal. */ \ || ((x).ldt.exponent == 0x0000U && (x).ldt.mantissah & 0x80000000UL)) /* Pseudo-Denormal. */ static __inline__ int todigit(char c) { - if (c<='0') return 0; - if (c>='9') return 9; + if (c <= '0') return 0; + if (c >= '9') return 9; return c-'0'; } static __inline__ char tochar(int n) { - if (n>=9) return '9'; - if (n<=0) return '0'; - return n+'0'; + if (n >= 9) return '9'; + if (n <= 0) return '0'; + return n + '0'; } /* have to deal with the negative buffer count kludge */ @@ -105,6 +106,7 @@ static char *exponentl(char *p, int expv static int isspeciall(long double d, char *bufp, int flags); #endif static __inline__ char * __grouping_format(char *string_start, char *string_end, char *buffer_end, int flags); +static __inline__ va_list __traverse_argument_list(int index_of_arg_to_be_fetched, const char *format_string, va_list arg_list); static char NULL_REP[] = "(null)"; static const char LOWER_DIGITS[] = "0123456789abcdef"; @@ -136,6 +138,10 @@ _doprnt(const char *fmt0, va_list argp, char buf[BUF]; /* space for %c, %[diouxX], %[aAeEfFgG] */ int neg_ldouble = FALSE; /* TRUE if _ldouble is negative */ struct lconv *locale_info; /* current locale information */ + int using_numeric_conv_spec; /* TRUE if using numeric specifier, FALSE else */ + va_list arg_list; /* argument list */ + va_list to_be_printed = NULL; /* argument to be printed if numeric specifier are used */ + const char *pos; /* position in format string when checking for numeric conv spec */ locale_info = localeconv(); @@ -149,6 +155,8 @@ _doprnt(const char *fmt0, va_list argp, if ((fp->_flag & _IOWRT) == 0) return (EOF); + using_numeric_conv_spec = FALSE; + arg_list = argp; fmt = fmt0; for (cnt = 0;; ++fmt) { @@ -197,6 +205,16 @@ _doprnt(const char *fmt0, va_list argp, * -- ANSI X3J11 * They don't exclude field widths read from args. */ + pos = fmt; + for (n = 0, fmt++; isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt); fmt++) + n = 10 * n + todigit(*fmt); + if (*fmt == '$') + { + using_numeric_conv_spec = TRUE; + argp = __traverse_argument_list(n, fmt0, arg_list); + } + else + fmt = pos; if ((width = va_arg(argp, int)) >= 0) goto rflag; width = -width; @@ -209,7 +227,19 @@ _doprnt(const char *fmt0, va_list argp, goto rflag; case '.': if (*++fmt == '*') - n = va_arg(argp, int); + { + pos = fmt; + for (n = 0, fmt++; isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt); fmt++) + n = 10 * n + todigit(*fmt); + if (*fmt == '$') + { + using_numeric_conv_spec = TRUE; + argp = __traverse_argument_list(n, fmt0, arg_list); + } + else + fmt = pos; + n = va_arg(argp, int); + } else { n = 0; @@ -233,21 +263,30 @@ _doprnt(const char *fmt0, va_list argp, do { n = 10 * n + todigit(*fmt); } while (isascii((unsigned char)*++fmt) && isdigit((unsigned char)*fmt)); - width = n; - --fmt; + if (*fmt == '$') + { + using_numeric_conv_spec = TRUE; + to_be_printed = __traverse_argument_list(n, fmt0, arg_list); + } + else + { + width = n; + --fmt; + } goto rflag; case 'L': flags |= LONGDBL; goto rflag; case 'h': - if (flags & SHORTINT) { + if (flags & SHORTINT) + { /* C99 */ /* for 'hh' - char */ flags |= CHARINT; flags &= ~SHORTINT; - } else { - flags |= SHORTINT; } + else + flags |= SHORTINT; goto rflag; case 'l': if (flags & LONGINT) @@ -274,6 +313,8 @@ _doprnt(const char *fmt0, va_list argp, /*FALLTHROUGH*/ case 'd': case 'i': + if (using_numeric_conv_spec) + argp = to_be_printed; ARG(signed); if ((long long)_ulonglong < 0) { @@ -292,6 +333,8 @@ _doprnt(const char *fmt0, va_list argp, case 'e': case 'f': case 'g': + if (using_numeric_conv_spec) + argp = to_be_printed; flags |= FLOAT; if (*fmt == 'A' || *fmt == 'a') { @@ -378,6 +421,8 @@ _doprnt(const char *fmt0, va_list argp, base = flags & HEXPREFIX ? 16 : 10; goto pforw; case 'n': + if (using_numeric_conv_spec) + argp = to_be_printed; if (flags & LONGDBL) *va_arg(argp, long long *) = cnt; else if (flags & LONGINT) @@ -393,6 +438,8 @@ _doprnt(const char *fmt0, va_list argp, flags |= LONGINT; /*FALLTHROUGH*/ case 'o': + if (using_numeric_conv_spec) + argp = to_be_printed; ARG(unsigned); base = 8; flags |= FINITENUMBER; @@ -406,10 +453,14 @@ _doprnt(const char *fmt0, va_list argp, * -- ANSI X3J11 */ /* NOSTRICT */ + if (using_numeric_conv_spec) + argp = to_be_printed; _ulonglong = (unsigned long)va_arg(argp, void *); base = 16; goto nosign; case 's': + if (using_numeric_conv_spec) + argp = to_be_printed; if (!(t = va_arg(argp, char *))) t = NULL_REP; if (prec >= 0) @@ -438,6 +489,8 @@ _doprnt(const char *fmt0, va_list argp, flags |= LONGINT; /*FALLTHROUGH*/ case 'u': + if (using_numeric_conv_spec) + argp = to_be_printed; ARG(unsigned); base = 10; flags |= FINITENUMBER; @@ -446,6 +499,8 @@ _doprnt(const char *fmt0, va_list argp, flags |= UPPERCASE; /* FALLTHROUGH */ case 'x': + if (using_numeric_conv_spec) + argp = to_be_printed; ARG(unsigned); base = 16; flags |= FINITENUMBER; @@ -1251,3 +1306,158 @@ __grouping_format(char *string_start, ch return (*dst == thousands_sep) ? dst + 1 : dst; /* Remove leading thousands separator character. */ } + +static __inline__ va_list +__traverse_argument_list(int index_of_arg_to_be_fetched, const char *fmt0, va_list argp) +{ + /* + * Traverse an argument list until certain position. + * The argument list of type va_list is traversed + * according to the information extracted from the + * format string fmt. It stops at position given by + * index_of_arg_to_be_fetched so the next access by + * the calling context using va_arg() fetches the + * wanted element from the list. + */ + + const char *fmt = fmt0; + int arg_index = 0, prec_index = 0, list_index; + int ch, flags, n = index_of_arg_to_be_fetched + 1; + unsigned long long _ulonglong; /* discard value retrieved by ARG() */ + + + for (list_index = 1; list_index < n;) + { + while ((ch = *fmt) && ch != '%') + fmt++; + if (!ch) + { + if (list_index < n) + fmt = fmt0; + else + return argp; + } + + flags = 0; + rflag: + switch (*++fmt) + { + /* Flags */ + case ' ': case '#': case '\'': + case '-': case '+': + goto rflag; + + /* Numeric conversion specifier field width and precision: *n$.*m$ */ + case '*': + for (prec_index = 0, fmt++; isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt); fmt++) + prec_index = 10 * prec_index + todigit(*fmt); + if (prec_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (prec_index == list_index) + { + va_arg(argp, int); /* discard */ + list_index++; + } + goto rflag; + case '.': + if (*++fmt == '*') + { + for (prec_index = 0, fmt++; isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt); fmt++) + prec_index = 10 * prec_index + todigit(*fmt); + if (prec_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (prec_index == list_index) + { + va_arg(argp, int); /* discard */ + list_index++; + } + } + goto rflag; + + /* Numeric conversion specifier %n$ */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + for (arg_index = 0; isascii((unsigned char)*fmt) && isdigit((unsigned char)*fmt); fmt++) + arg_index = 10 * arg_index + todigit(*fmt); + goto rflag; + + /* Length modifiers */ + case 'h': case 't': case 'z': /* promoted to int */ + goto rflag; + case 'L': /* a, A, e, E, f, F, g, G applies to long double */ + flags |= LONGDBL; + goto rflag; + case 'l': + if (flags & LONGINT) + flags |= LONGDBL; /* d, i o, u, x, X applies to long long */ + else + flags |= LONGINT; /* d, i o, u, x, X applies to long */ + goto rflag; + case 'j': /* d, i o, u, x, X applies to intmax_t */ + flags |= LONGDBL; /* long long */ + goto rflag; + + /* Conversion specifiers */ + case 'c': + if (arg_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (arg_index == list_index) + { + va_arg(argp, int); /* discard */ + list_index++; + } + break; + case 'D': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'd': case 'i': + if (arg_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (arg_index == list_index) + { + ARG(signed); /* discard */ + list_index++; + } + break; + case 'A': case 'E': case 'F': case 'G': + case 'a': case 'e': case 'f': case 'g': + if (arg_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (arg_index == list_index) + { + if (flags & LONGDBL) + va_arg(argp, long double); /* discard */ + else + va_arg(argp, double); /* discard */ + list_index++; + } + break; + case 'O': case 'U': case 'X': + flags |= LONGINT; + /*FALLTHROUGH*/ + case 'o': case 'u': case 'x': + if (arg_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (arg_index == list_index) + { + ARG(unsigned); /* discard */ + list_index++; + } + break; + case 'n': case 'p': case 's': + if (arg_index == index_of_arg_to_be_fetched && list_index == index_of_arg_to_be_fetched) + return argp; + if (arg_index == list_index) + { + va_arg(argp, void *); /* discard */ + list_index++; + } + break; + + case '\0': + return argp; + } + } + + return argp; +} diff -aprNU3 djgpp.orig/src/libc/ansi/stdio/printf.txh djgpp/src/libc/ansi/stdio/printf.txh --- djgpp.orig/src/libc/ansi/stdio/printf.txh 2008-04-24 15:09:44 +0000 +++ djgpp/src/libc/ansi/stdio/printf.txh 2008-04-24 15:13:20 +0000 @@ -13,8 +13,23 @@ int printf(const char *@var{format}, @do Sends formatted output from the arguments (@dots{}) to @code{stdout}. The format string contains regular characters to print, as well as -conversion specifiers, which begin with a percent symbol. Each -conversion speficier contains the following fields: +conversion specifiers, which begin with a percent symbol. Conversions can +be applied to the nth argument after the format string in the argument list, +rather than to the next unused argument. In this case, the conversion +specifier character @code{%} is replaced by the sequence @code{"%n$"}, where +@code{n} is a decimal integer in the range [@code{1}, @code{number of the last argument}], +giving the position of the argument in the argument list. The format string +can contain either numbered argument conversion specifiers (that is, +@code{"%n$"} +and @code{"*m$"}), or unnumbered argument conversion specifiers (that is @code{%} +and @code{*}), but not both. The only exception to this is that @code{%%} can +be mixed with @code{"%n$"} form. When numbered argument specifications are +used, specifying the Nth argument requires that all the leading arguments, +from the first to the (N-1)th, are specified in the format string. In format +strings containing the @code{"%n$"} form of conversion specification, numbered +arguments in the argument list can be referenced from the format string as many +times as required. + +Each conversion specifier contains the following fields: @itemize @bullet @@ -59,6 +74,11 @@ A field width specifier, which specifies This may also be an asterisk (@code{*}), which means that the actual width will be obtained from the next argument. If the argument is negative, it supplies a @code{-} flag and a positive width. +In format strings containing the @code{"%n$"} form of a conversion +specifier, a field width can be indicated by the sequence @code{"*m$"}, +where m is a decimal integer in the range [@code{1}, @code{number of the last argument}], +giving the position in the argument list (after the format argument) of +an integer argument containing the field width. @item @@ -68,6 +88,11 @@ The precision specifies the minimum numb integer, the number of fraction digits for a floating point number (max for @code{g} or @code{G}, actual for others), or the maximum number of characters for a string. +In format strings containing the @code{"%n$"} form of a conversion +specifier, a precision can be indicated by the sequence @code{"*m$"}, +where m is a decimal integer in the range [@code{1}, @code{number of the last argument}], +giving the position in the argument list (after the format argument) of +an integer argument containing the precision. @item @@ -221,3 +246,37 @@ specifiers first appeared in the ANSI C9 @example printf("%-3d %10.2f%% Percent of %s\n", index, per[index], name[index]); @end example + +The following statement can be used to print date and time +using language-independent format: + +@example +printf(format, weekday, month, day, hour, precision, min); +@end example + + +For American usage, the format string could look like: + +@example +"%s, %s %d, %d:%.*d\n" +@end example + +The integer precision has the value 2. +The above example will produce the following message: + +@example +Sunday, October 27, 9:09 +@end example + + +For German usage, the format string could look like: + +@example +"%1$s, %3$d. %2$s, %4$d:%6$.*5$d\n" +@end example + +The above example will produce the following message: + +@example +Sonntag, 27. Oktober, 9:09 +@end example diff -aprNU3 djgpp.orig/tests/libc/ansi/stdio/makefile djgpp/tests/libc/ansi/stdio/makefile --- djgpp.orig/tests/libc/ansi/stdio/makefile 2008-04-24 15:09:08 +0000 +++ djgpp/tests/libc/ansi/stdio/makefile 2008-04-24 15:13:20 +0000 @@ -17,6 +17,7 @@ SRC += mktemp.c SRC += printf.c SRC += printf2.c SRC += printf3.c +SRC += printf4.c SRC += sscanf.c SRC += sscanf2.c SRC += sscanf3.c diff -aprNU3 djgpp.orig/tests/libc/ansi/stdio/printf4.c djgpp/tests/libc/ansi/stdio/printf4.c --- djgpp.orig/tests/libc/ansi/stdio/printf4.c 1970-01-01 00:00:00 +0000 +++ djgpp/tests/libc/ansi/stdio/printf4.c 2008-04-24 14:33:00 +0000 @@ -0,0 +1,66 @@ +/* + * printf4.c + * Test cases for numeric conversion specifiers. + */ + +#include +#include +#include + +int main(void) +{ + int i, width, precision; + double darg[] = {1.1, 2.2, 3.3}; + const char *title[] = {"En castellano:", "Auf deutsch:", "In english:"}; + const char *format[] = { + "%5$s, %2$d de %1$s, %3$*6$.*7$d:%4$*6$.*7$d\n", + "%5$s, %2$d. %1$s, %3$*6$.*7$d:%4$*6$.*7$d\n", + "%5$s, %1$s %2$d, %3$*6$.*7$d:%4$*6$.*7$d\n" + }; + const char *weekday[] = {"Sabado", "Samstag", "Saturday"}; + const char *month[] = {"febrero", "Februar", "february"}; + int day = 2; + int hour = 12; + int min = 34; + + + printf("Testing numeric conversion specifiers.\n" + "======================================\n"); + + width = 10; + precision = 1; + printf("Printing a sequence of numbers using a given field width and precision\n" + "accessing the variables a multiple times in different order.\n" + "The sequence of arguments after the format string is:\n" + " width, precision, darg[0], darg[1], darg[2]\n" + "with the values:\n" + " width: %d\n" + " precision: %d\n" + " darg[0]: %f\n" + " darg[1]: %f\n" + " darg[2]: %f\n", + width, precision, darg[0], darg[1], darg[2]); + printf("Format string: \"%%3$-*1$.*2$f###%%4$*1$.*2$f###%%5$*1$.*2$f\" <%3$-*1$.*2$f###%4$*1$.*2$f###%5$*1$.*2$f>\n" + "Printing again but accessing the arguments in inverse order:\n" + "Format string: \"%%5$-*1$.*2$f###%%4$*1$.*2$f###%%3$*1$.*2$f\" <%5$-*1$.*2$f###%4$*1$.*2$f###%3$*1$.*2$f>\n\n\n", + width, precision, darg[0], darg[1], darg[2]); + + + + width = 2; + precision = 2; + printf("Printing Language-Independent Date and Time.\n\n"); + for (i = 0; i < 3; i++) + { + char *fmt; + int len = strlen(format[i]); + printf("%s ", title[i]); + printf(format[i], month[i], day, hour, min, weekday[i], width, precision); + fmt = unconst((&format[i][--len]), char *); + *fmt = '\0'; + printf("Produced with:\n" + " printf(\"%1$s\\n\", month[%2$i], day, hour, min, weekday[%2$i], width, precision);\n\n", + format[i], i); + } + return 0; +} diff -aprNU3 djgpp.orig/tests/libc/ansi/stdio/printf5.c djgpp/tests/libc/ansi/stdio/printf5.c --- djgpp.orig/tests/libc/ansi/stdio/printf5.c 1970-01-01 00:00:00 +0000 +++ djgpp/tests/libc/ansi/stdio/printf5.c 2008-04-24 15:06:16 +0000 @@ -0,0 +1,839 @@ +/* + * This program test flags, conversion modifiers and conversion specifiers + * of the printf familiy of function. During compilation some warnings + * will be generated, this is part of the test and is intentional. Thus + * the program can not be compiled with the stock makefile because this + * stops at warnings. You must manually compile the program taking care + * that the library to be tested is used when linking. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +void flags_test(FILE *out) +{ + fprintf(out, "Testing all flags.\n" + "==================\n"); + fprintf(out, "Flag: \"'\"\n" + " The integer portion of the result of a decimal conversion (%%i, %%d, %%u, %%f,\n" + " %%F, %%g, or %%G) shall be formatted with thousands' grouping characters.\n" + " For other conversions the behavior is undefined. The non-monetary grouping\n" + " character is used.\n" + "arg: %d format string: \"%%'d\" <%'d>\n" + "arg: %d format string: \"%%'i\" <%'i>\n" + "arg: %d format string: \"%%'u\" <%'u>\n" + "arg: %.1f format string: \"%%'f\" <%'f>\n" + "arg: %.1f format string: \"%%'F\" <%'F>\n" + "arg: %.1f format string: \"%%'g\" <%'g>\n" + "arg: %.1f format string: \"%%'G\" <%'G>\n\n", + 1, 1, + 1, 1, + 1, 1, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0); + + + + fprintf(out, "Flag: \"-\"\n" + " The result of the conversion shall be left-justified within the field. The\n" + " conversion is right-justified if this flag is not specified.\n" + "arg: %d format string: \"%%-5d\" <%-5d>\n" + "arg: %d format string: \"%%5d\" <%5d>\n" + "arg: %d format string: \"%%-5i\" <%-5i>\n" + "arg: %d format string: \"%%5i\" <%5i>\n" + "arg: %d format string: \"%%-5o\" <%-5o>\n" + "arg: %d format string: \"%% 5o\" <%5o>\n" + "arg: %d format string: \"%%-5u\" <%-5u>\n" + "arg: %d format string: \"%%5u\" <%5u>\n" + "arg: %d format string: \"%%-5x\" <%-5x>\n" + "arg: %d format string: \"%%5X\" <%5X>\n" + "arg: %.1f format string: \"%%-10a\" <%-10a>\n" + "arg: %.1f format string: \"%%10A\" <%10A>\n" + "arg: %.1f format string: \"%%-15e\" <%-15e>\n" + "arg: %.1f format string: \"%%15E\" <%15E>\n" + "arg: %.1f format string: \"%%-10f\" <%-10f>\n" + "arg: %.1f format string: \"%%10F\" <%10F>\n" + "arg: %.1f format string: \"%%-10g\" <%-10g>\n" + "arg: %.1f format string: \"%%10G\" <%10G>\n\n", + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 10, 10, + 15, 15, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0); + + + + fprintf(out, "Flag: \"+\"\n" + " The result of a signed conversion shall always begin with a sign ('+' or\n" + " '-'). The conversion shall begin with a sign only when a negative value\n" + " is converted if this flag is not specified.\n" + "arg: %d format string: \"%%+d\" <%+d>\n" + "arg: %d format string: \"%%i\" <%i>\n" + "arg: %d format string: \"%%+o\" <%+o> WARNING: Sign flag used with unsigned magnitude. Flag ignored.\n" + "arg: %d format string: \"%%+u\" <%+u> WARNING: Sign flag used with unsigned magnitude. Flag ignored.\n" + "arg: %d format string: \"%%+x\" <%+x> WARNING: Sign flag used with unsigned magnitude. Flag ignored.\n" + "arg: %d format string: \"%%+X\" <%+X> WARNING: Sign flag used with unsigned magnitude. Flag ignored.\n" + "arg: %.1f format string: \"%%+a\" <%+a>\n" + "arg: %.1f format string: \"%%A\" <%A>\n" + "arg: %.1f format string: \"%%+e\" <%+e>\n" + "arg: %.1f format string: \"%%E\" <%E>\n" + "arg: %.1f format string: \"%%+f\" <%+f>\n" + "arg: %.1f format string: \"%%F\" <%F>\n" + "arg: %.1f format string: \"%%+g\" <%+g>\n" + "arg: %.1f format string: \"%%G\" <%G>\n\n", + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 10, 10, + 15, 15, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0); + + + + fprintf(out, "Flag: \"\"\n" + " If the first character of a signed conversion is not a sign or if a signed\n" + " conversion results in no characters, a shall be prefixed to the\n" + " result. This means that if the and '+' flags both appear, the\n" + " flag shall be ignored.\n" + "arg: %d format string: \"%% i\" <% i>\n" + "arg: %d format string: \"%%+ i\" <%+ i> WARNING: Sign and space flags used simultaneously. Space flag ignored.\n" + "arg: %d format string: \"%% d\" <% d>\n" + "arg: %d format string: \"%% +d\" <% +d> WARNING: Sign and space flags used simultaneously. Space flag ignored.\n" + "arg: %d format string: \"%% o\" <% o> WARNING: Space flag used with unsigned magnitude. Flag ignored.\n" + "arg: %d format string: \"%%+ o\" <%+ o> WARNING: Sign and space flags used with unsigned magnitude. Flags ignored.\n" + "arg: %d format string: \"%% u\" <% u> WARNING: Space flag used with unsigned magnitude. Flag ignored.\n" + "arg: %d format string: \"%%+ u\" <%+ u> WARNING: Sign and space flags used with unsigned magnitude. Flags ignored.\n" + "arg: %d format string: \"%% x\" <% x> WARNING: Space flag used with unsigned magnitude. Flag ignored.\n" + "arg: %d format string: \"%%+ X\" <%+ X> WARNING: Sign and space flags used with unsigned magnitude. Flags ignored.\n" + "arg: %.1f format string: \"%% a\" <% a>\n" + "arg: %.1f format string: \"%%+ A\" <%+ A> WARNING: Sign and space flags used simultaneously. Space flag ignored.\n" + "arg: %.1f format string: \"%% e\" <% e>\n" + "arg: %.1f format string: \"%% +E\" <% +E> WARNING: Sign and space flags used simultaneously. Space flag ignored.\n" + "arg: %.1f format string: \"%% f\" <% f>\n" + "arg: %.1f format string: \"%%+ F\" <%+ F> WARNING: Sign and space flags used simultaneously. Space flag ignored.\n" + "arg: %.1f format string: \"%% g\" <% g>\n" + "arg: %.1f format string: \"%% +G\" <% +G> WARNING: Sign and space flags used simultaneously. Space flag ignored.\n\n", + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 10, 10, + 15, 15, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0); + + + + fprintf(out, "Flag: \"#\"\n" + " Specifies that the value is to be converted to an alternative form. For o\n" + " conversion, it increases the precision (if necessary) to force the first\n" + " digit of the result to be zero. For x or X conversion specifiers, a non-\n" + " zero result shall have 0x (or 0X) prefixed to it. For a, A, e, E, f, F, g,\n" + " and G conversion specifiers, the result shall always contain a radix\n" + " character, even if no digits follow the radix character. Without this\n" + " flag, a radix character appears in the result of these conversions only if\n" + " a digit follows it. For g and G conversion specifiers, trailing zeros\n" + " shall not be removed from the result as they normally are. For other\n" + " conversion specifiers, the behavior is undefined.\n" + "arg: %d format string: \"%%#d\" <%#d> WARNING: # flag used with decimal magnitude. Flag ignored.\n" + "arg: %d format string: \"%%#i\" <%#i> WARNING: # flag used with decimal magnitude. Flag ignored.\n" + "arg: %d format string: \"%%#u\" <%#u> WARNING: # flag used with decimal magnitude. Flag ignored.\n" + "arg: %d format string: \"%%#o\" <%#o>\n" + "arg: %d format string: \"%%#o\" <%#o>\n" + "arg: %d format string: \"%%#x\" <%#x>\n" + "arg: %d format string: \"%%#X\" <%#X>\n" + "arg: %.1f format string: \"%%a\" <%a>\n" + "arg: %.1f format string: \"%%#A\" <%#A>\n" + "arg: %.1f format string: \"%%1.e\" <%1.e>\n" + "arg: %.1f format string: \"%%#1.E\" <%#1.E>\n" + "arg: %.1f format string: \"%%1.f\" <%1.f>\n" + "arg: %.1f format string: \"%%#1.F\" <%#1.F>\n" + "arg: %.1f format string: \"%%g\" <%g>\n" + "arg: %.1f format string: \"%%#G\" <%#G>\n\n", + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 0, 0, + 1, 1, + 0, 0, + 1.0, 1.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 0.0, + 1.0, 1.0, + 0.0, 0.0); + + + + fprintf(out, "Flag: \"0\"\n" + " For d, i, o, u, x, X, a, A, e, E, f, F, g, and G conversion specifiers,\n" + " leading zeros (following any indication of sign or base) are used to pad\n" + " to the field width; no space padding is performed. If the '0' and '-'\n" + " flags both appear, the '0' flag is ignored. For d, i, o, u, x, and X\n" + " conversion specifiers, if a precision is specified, the '0' flag is\n" + " ignored. If the '0' and ''' flags both appear, the grouping characters are\n" + " inserted before zero padding. For other conversions, the behavior is\n" + " undefined.\n" + "arg: %d format string: \"%%05d\" <%05d>\n" + "arg: %d format string: \"%%-05d\" <%-05d> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %d format string: \"%%05.d\" <%05.d>\n" + "arg: %d format string: \"%%05i\" <%05i>\n" + "arg: %d format string: \"%%-05i\" <%-05i> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %d format string: \"%%05.i\" <%05.i>\n" + "arg: %d format string: \"%%05o\" <%05o>\n" + "arg: %d format string: \"%%-05o\" <%-05o> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %d format string: \"%%05.o\" <%05.o>\n" + "arg: %d format string: \"%%05u\" <%05u>\n" + "arg: %d format string: \"%%-05u\" <%-05u> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %d format string: \"%%05.u\" <%05.u>\n" + "arg: %d format string: \"%%05x\" <%05x>\n" + "arg: %d format string: \"%%-05x\" <%-05x> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %d format string: \"%%05.x\" <%05.x>\n" + "arg: %d format string: \"%%05X\" <%05X>\n" + "arg: %d format string: \"%%-05X\" <%-05X> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %d format string: \"%%05.X\" <%05.X>\n" + "arg: %.1f format string: \"%%0-10a\" <%0-10a> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %.1f format string: \"%%0+10A\" <%0+10A>\n" + "arg: %.1f format string: \"%%010A\" <%010A>\n" + "arg: %.1f format string: \"%%0-15e\" <%0-15e> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %.1f format string: \"%%0+15E\" <%0+15E>\n" + "arg: %.1f format string: \"%%015E\" <%015E>\n" + "arg: %.1f format string: \"%%0-10f\" <%0-10f> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %.1f format string: \"%%0+10F\" <%0+10F>\n" + "arg: %.1f format string: \"%%010F\" <%010F>\n" + "arg: %.1f format string: \"%%0-10g\" <%0-10g> WARNING: - and 0 flags used simultaneously. 0 flag ignored.\n" + "arg: %.1f format string: \"%%0+10G\" <%0+10G>\n" + "arg: %.1f format string: \"%%010G\" <%010G>\n\n", + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 1, 1, + 10, 10, + 10, 10, + 10, 10, + 15, 15, + 15, 15, + 15, 15, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0, + 1.0, 1.0); +} + + +void length_modifiers_test(FILE *out) +{ + char cv, *pcv; + short int siv, *psiv; + long int liv, *pliv; + long long int lliv, *plliv; + intmax_t jiv, *pjiv; + size_t ziv, *pziv; + ptrdiff_t piv, *ppiv; + pcv = &cv; + psiv = &siv; + pliv = &liv; + plliv = &lliv; + pjiv = &jiv; + pziv = &ziv; + ppiv = ϖ + + + fprintf(out, "\n\nTesting all length modifiers.\n" + "=============================\n"); + fprintf(out, "Length modifier: \"hh\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to a signed char or unsigned char argument\n" + " (the argument will have been promoted according to the integer\n" + " promotions, but its value shall be converted to signed char or\n" + " unsigned char before printing); or that a following n conversion\n" + " specifier applies to a pointer to a signed char argument.\n" + "arg: CHAR_MAX format string: \"%%hhd\" <%hhd>\n" + "arg: CHAR_MIN format string: \"%%hhi\" <%hhi>\n" + "arg: UCHAR_MAX format string: \"%%hho\" <%hho>\n" + "arg: UCHAR_MAX format string: \"%%hhu\" <%hhu>\n" + "arg: CHAR_MIN format string: \"%%hhx\" <%hhx>\n" + "arg: CHAR_MAX format string: \"%%hhX\" <%hhX>\n" + "arg: - format string: \"%%hhn\" (pointer to cv)%hhn\n", + CHAR_MAX, + CHAR_MIN, + UCHAR_MAX, + UCHAR_MAX , + CHAR_MIN, + CHAR_MAX, + pcv); + fprintf(out, "cv=%i\n\n", *pcv); + + + + fprintf(out, "Length modifier: \"h\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to a short or unsigned short argument (the\n" + " argument will have been promoted according to the integer\n" + " promotions, but its value shall be converted to short or\n" + " unsigned short before printing); or that a following n\n" + " conversion specifier applies to a pointer to a short argument.\n" + "arg: SHRT_MAX format string: \"%%hd\" <%hd>\n" + "arg: SHRT_MIN format string: \"%%hi\" <%hi>\n" + "arg: USHRT_MAX format string: \"%%ho\" <%ho>\n" + "arg: USHRT_MAX format string: \"%%hu\" <%hu>\n" + "arg: SHRT_MIN format string: \"%%hx\" <%hx>\n" + "arg: SHRT_MAX format string: \"%%hX\" <%hX>\n" + "arg: - format string: \"%%hn\" (pointer to siv)%hn\n", + SHRT_MAX, + SHRT_MIN, + USHRT_MAX, + USHRT_MAX, + SHRT_MIN, + SHRT_MAX, + psiv); + fprintf(out, "siv=%i\n\n", *psiv); + + + + fprintf(out, "Length modifier: \"l\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to a long or unsigned long argument; that a\n" + " following n conversion specifier applies to a pointer to a long\n" + " argument; that a following c conversion specifier applies to a\n" + " wint_t argument; that a following s conversion specifier\n" + " applies to a pointer to a wchar_t argument; or has no effect on\n" + " a following a, A, e, E, f, F, g, or G conversion specifier.\n" + "arg: LONG_MAX format string: \"%%ld\" <%ld>\n" + "arg: LONG_MIN format string: \"%%li\" <%li>\n" + "arg: ULONG_MAX format string: \"%%lo\" <%lo>\n" + "arg: ULONG_MAX format string: \"%%lu\" <%lu>\n" + "arg: LONG_MIN format string: \"%%lx\" <%lx>\n" + "arg: LONG_MAX format string: \"%%lX\" <%lX>\n" + "arg: - format string: \"%%ln\" (pointer to liv)%ln\n" + "arg: %.1f format string: \"%%la\" <%la>\n" + "arg: %.1f format string: \"%%A\" <%A>\n" + "arg: %.1f format string: \"%%le\" <%le>\n" + "arg: %.1f format string: \"%%E\" <%E>\n" + "arg: %.1f format string: \"%%lf\" <%lf>\n" + "arg: %.1f format string: \"%%F\" <%F>\n" + "arg: %.1f format string: \"%%lg\" <%lg>\n" + "arg: %.1f format string: \"%%G\" <%G>\n", + LONG_MAX, + LONG_MIN, + ULONG_MAX, + ULONG_MAX, + LONG_MIN, + LONG_MAX, + pliv, + 1.1, 1.1, + 1.1, 1.1, + 2.2, 2.2, + 2.2, 2.2, + 3.3, 3.3, + 3.3, 3.3, + 4.4, 4.4, + 4.4, 4.4); + fprintf(out, "liv=%li\n\n", *pliv); + + + + fprintf(out, "Length modifier: \"ll\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to a long long or unsigned long long argument;\n" + " or that a following n conversion specifier applies to a pointer\n" + " to a long long argument.\n" + "arg: LONG_LONG_MAX format string: \"%%lld\" <%lld>\n" + "arg: LONG_LONG_MIN format string: \"%%lli\" <%lli>\n" + "arg: ULONG_LONG_MAX format string: \"%%llo\" <%llo>\n" + "arg: ULONG_LONG_MAX format string: \"%%llu\" <%llu>\n" + "arg: LONG_LONG_MIN format string: \"%%llx\" <%llx>\n" + "arg: LONG_LONG_MAX format string: \"%%llX\" <%llX>\n" + "arg: - format string: \"%%lln\" (pointer to lliv)%lln\n", + LONG_LONG_MAX, + LONG_LONG_MIN, + ULONG_LONG_MAX, + ULONG_LONG_MAX, + LONG_LONG_MIN, + LONG_LONG_MAX, + plliv); + fprintf(out, "lliv=%lli\n\n", *plliv); + + + + fprintf(out, "Length modifier: \"j\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to an intmax_t or uintmax_t argument; or that\n" + " a following n conversion specifier applies to a pointer to an\n" + " intmax_t argument.\n" + "arg: LONG_LONG_MAX format string: \"%%jd\" <%jd>\n" + "arg: LONG_LONG_MIN format string: \"%%ji\" <%ji>\n" + "arg: ULONG_LONG_MAX format string: \"%%jo\" <%jo>\n" + "arg: ULONG_LONG_MAX format string: \"%%ju\" <%ju>\n" + "arg: LONG_LONG_MIN format string: \"%%jx\" <%jx>\n" + "arg: LONG_LONG_MAX format string: \"%%jX\" <%jX>\n" + "arg: - format string: \"%%jn\" (pointer to jiv)%jn\n", + (intmax_t)LONG_LONG_MAX, + (intmax_t)LONG_LONG_MIN, + (uintmax_t)ULONG_LONG_MAX, + (uintmax_t)ULONG_LONG_MAX, + (intmax_t)LONG_LONG_MIN, + (intmax_t)LONG_LONG_MAX, + pjiv); + fprintf(out, "jiv=%ji\n\n", *pjiv); + + + + fprintf(out, "Length modifier: \"z\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to a size_t or the corresponding signed\n" + " integer type argument; or that a following n conversion\n" + " specifier applies to a pointer to a signed integer type\n" + " corresponding to a size_t argument.\n" + "arg: LONG_MAX format string: \"%%zd\" <%zd>\n" + "arg: LONG_MIN format string: \"%%zi\" <%zi>\n" + "arg: LONG_MAX format string: \"%%zo\" <%zo>\n" + "arg: LONG_MAX format string: \"%%zu\" <%zu>\n" + "arg: LONG_MIN format string: \"%%zx\" <%zx>\n" + "arg: LONG_MAX format string: \"%%zX\" <%zX>\n" + "arg: - format string: \"%%zn\" (pointer to ziv)%zn\n", + (size_t)LONG_MAX, + (size_t)LONG_MIN, + (size_t)LONG_MAX, + (size_t)LONG_MAX, + (size_t)LONG_MIN, + (size_t)LONG_MAX, + pziv); + fprintf(out, "ziv=%zi\n\n", *pziv); + + + + fprintf(out, "Length modifier: \"t\"\n" + " Specifies that a following d, i, o, u, x, or X conversion\n" + " specifier applies to a ptrdiff_t or the corresponding\n" + " unsigned type argument; or that a following n conversion\n" + " specifier applies to a pointer to a ptrdiff_t argument.\n" + "arg: LONG_MAX format string: \"%%td\" <%td>\n" + "arg: LONG_MIN format string: \"%%ti\" <%ti>\n" + "arg: LONG_MAX format string: \"%%to\" <%to>\n" + "arg: ULONG_MAX format string: \"%%tu\" <%tu>\n" + "arg: LONG_MIN format string: \"%%tx\" <%tx>\n" + "arg: LONG_MAX format string: \"%%tX\" <%tX>\n" + "arg: - format string: \"%%tn\" (pointer to piv)%tn\n", + (ptrdiff_t)LONG_MAX, + (ptrdiff_t)LONG_MIN, + (ptrdiff_t)LONG_MAX, + (ptrdiff_t)ULONG_MAX, + (ptrdiff_t)LONG_MIN, + (ptrdiff_t)LONG_MAX, + ppiv); + fprintf(out, "piv=%ti\n\n", *ppiv); + + + + fprintf(out, "Length modifier: \"L\"\n" + "---------1---------2---------3---------4---------5---------6---------7---------8\n" + " Specifies that a following a, A, e, E, f, F, g, or G conversion\n" + " specifier applies to a long double argument.\n" + "arg: LDBL_MAX format string: \"%%La\" <%La>\n" + "arg: LDBL_MIN format string: \"%%LA\" <%LA>\n" + "arg: LDBL_MAX format string: \"%%Le\" <%Le>\n" + "arg: LDBL_MIN format string: \"%%LE\" <%LE>\n" + "arg: LDBL_MAX format string: \"%%Lf\" <%Lf>\n" + "arg: LDBL_MIN format string: \"%%LF\" <%LF>\n" + "arg: LDBL_MAX format string: \"%%Lg\" <%Lg>\n" + "arg: LDBL_MIN format string: \"%%LG\" <%LG>\n\n", + LDBL_MAX, + LDBL_MIN, + LDBL_MAX, + LDBL_MIN, + LDBL_MAX, + LDBL_MIN, + LDBL_MAX, + LDBL_MIN); +} + + +void numeric_conversion_specifiers_test(FILE *out) +{ + int i, width, precision; + double darg[] = {1.1, 2.2, 3.3}; + char *title[] = {"En castellano:", "Auf deutsch:", "In english:"}; + char *format[] = { + "%5$s, %2$d de %1$s, %3$*6$.*7$d:%4$*6$.*7$d\n", + "%5$s, %2$d. %1$s, %3$*6$.*7$d:%4$*6$.*7$d\n", + "%5$s, %1$s %2$d, %3$*6$.*7$d:%4$*6$.*7$d\n" + }; + char *weekday[] = {"Sabado", "Samstag", "Saturday"}; + char *month[] = {"febrero", "Februar", "february"}; + int day = 2; + int hour = 12; + int min = 34; + + + fprintf(out, "\n\nTesting numeric conversion specifiers.\n" + "======================================\n"); + + width = 10; + precision = 1; + fprintf(out, "Printing a sequence of numbers using a given field width and precision\n" + "accessing the variables a multiple times in different order.\n" + "The sequence of arguments after the format string is:\n" + " width, precision, darg[0], darg[1], darg[2]\n" + "with the values:\n" + " width: %d\n" + " precision: %d\n" + " darg[0]: %f\n" + " darg[1]: %f\n" + " darg[2]: %f\n", + width, precision, darg[0], darg[1], darg[2]); + fprintf(out, "Format string: \"%%3$-*1$.*2$f###%%4$*1$.*2$f###%%5$*1$.*2$f\" <%3$-*1$.*2$f###%4$*1$.*2$f###%5$*1$.*2$f>\n" + "Printing again but accessing the arguments in inverse order:\n" + "Format string: \"%%5$-*1$.*2$f###%%4$*1$.*2$f###%%3$*1$.*2$f\" <%5$-*1$.*2$f###%4$*1$.*2$f###%3$*1$.*2$f>\n\n\n", + width, precision, darg[0], darg[1], darg[2]); + + + width = 2; + precision = 2; + fprintf(out, "Printing Language-Independent Date and Time.\n\n"); + for (i = 0; i < 3; i++) + { + int len = strlen(format[i]); + fprintf(out, "%s ", title[i]); + fprintf(out, format[i], month[i], day, hour, min, weekday[i], width, precision); + format[i][--len] = '\0'; + fprintf(out, "Produced with:\n" + " printf(\"%1$s\\n\", month[%2$i], day, hour, min, weekday[%2$i], width, precision);\n\n", + format[i], i); + } +} + + +void printf_test(FILE *out) +{ + char buf[100], *strbuf; + union { + unsigned int word[3]; + long double value; + } x; + int strlng; + + + + fprintf(out, "\n\nGeneral test of the printf familiy of functions.\n" + "================================================\n"); + + fprintf(out, "Infinity.\n"); + sprintf(buf, "%La", 1.0L / 0.0L); + fprintf(out, "fmt str: \"%%La\" arg: 1.0L / 0.0L\n" + "Shall return: \"inf\" returns: \"%s\"\n", buf); + sprintf(buf, "%LA", -1.0L / 0.0L); + fprintf(out, "fmt str: \"%%LA\" arg: -1.0L / 0.0L\n" + "Shall return: \"-INF\" returns: \"%s\"\n", buf); + sprintf(buf, "%Lf", 1.0L / 0.0L); + fprintf(out, "fmt str: \"%%Lf\" arg: 1.0L / 0.0L\n" + "Shall return: \"inf\" returns: \"%s\"\n", buf); + sprintf(buf, "%LF", -1.0L / 0.0L); + fprintf(out, "fmt str: \"%%LF\" arg: -1.0L / 0.0L\n" + "Shall return: \"-INF\" returns: \"%s\"\n", buf); + sprintf(buf, "%Le", 1.0L / 0.0L); + fprintf(out, "fmt str: \"%%Le\" arg: 1.0L / 0.0L\n" + "Shall return: \"inf\" returns: \"%s\"\n", buf); + sprintf(buf, "%LE", -1.0L / 0.0L); + fprintf(out, "fmt str: \"%%LE\" arg: -1.0L / 0.0L\n" + "Shall return: \"-INF\" returns: \"%s\"\n", buf); + sprintf(buf, "%Lg", 1.0L / 0.0L); + fprintf(out, "fmt str: \"%%Lg\" arg: 1.0L / 0.0L\n" + "Shall return: \"inf\" returns: \"%s\"\n", buf); + sprintf(buf, "%LG", -1.0L / 0.0L); + fprintf(out, "fmt str: \"%%LG\" arg: -1.0L / 0.0L\n" + "Shall return: \"-INF\" returns: \"%s\"\n\n", buf); + + fprintf(out, "NaN.\n"); + sprintf(buf, "%La", 0.0L / 0.0L); + fprintf(out, "fmt str: \"%%La\" arg: 0.0L / 0.0L\n" + "Shall return: \"nan\" returns: \"%s\"\n", buf); + sprintf(buf, "%LA", -0.0L / 0.0L); + fprintf(out, "fmt str: \"%%LA\" arg: -0.0L / 0.0L\n" + "Shall return: \"NAN\" returns: \"%s\"\n", buf); + sprintf(buf, "%Lf", 0.0L / 0.0L); + fprintf(out, "fmt str: \"%%Lf\" arg: 0.0L / 0.0L\n" + "Shall return: \"nan\" returns: \"%s\"\n", buf); + sprintf(buf, "%LF", -0.0L / 0.0L); + fprintf(out, "fmt str: \"%%LF\" arg: -0.0L / 0.0L\n" + "Shall return: \"NAN\" returns: \"%s\"\n", buf); + sprintf(buf, "%Le", 0.0L / 0.0L); + fprintf(out, "fmt str: \"%%Le\" arg: 0.0L / 0.0L\n" + "Shall return: \"nan\" returns: \"%s\"\n", buf); + sprintf(buf, "%LE", -0.0L / 0.0L); + fprintf(out, "fmt str: \"%%LE\" arg: -0.0L / 0.0L\n" + "Shall return: \"NAN\" returns: \"%s\"\n", buf); + sprintf(buf, "%Lg", 0.0L / 0.0L); + fprintf(out, "fmt str: \"%%Lg\" arg: 0.0L / 0.0L\n" + "Shall return: \"nan\" returns: \"%s\"\n", buf); + sprintf(buf, "%LG", -0.0L / 0.0L); + fprintf(out, "fmt str: \"%%LG\" arg: -0.0L / 0.0L\n" + "Shall return: \"NAN\" returns: \"%s\"\n\n", buf); + + + x.word[2] = 0x7FFF; + x.word[1] = 0xC000000C; + x.word[0] = 0x10000001; + fprintf(out, "Quiet NaN\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + x.word[2] = 0x7FFF; + x.word[1] = 0x80000008; + x.word[0] = 0x10000001; + fprintf(out, "Signalling NaN\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + x.word[2] = 0x7FFF; + x.word[1] = 0x40000004; + x.word[0] = 0x10000001; + fprintf(out, "Pseudo-NaN\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + x.word[2] = 0xFFFF; + x.word[1] = 0x00000000; + x.word[0] = 0x00000000; + fprintf(out, "Pseudo-Infinity\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + x.word[2] = 0x4004; + x.word[1] = 0x00000000; + x.word[0] = 0x00000000; + fprintf(out, "Pseudo-Zero\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + x.word[2] = 0x0440; + x.word[1] = 0x60000006; + x.word[0] = 0x10000001; + fprintf(out, "Unnormalized number\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + x.word[2] = 0x0000; + x.word[1] = 0x80000008; + x.word[0] = 0x10000001; + fprintf(out, "Pseudo-Denormal\n"); + sprintf(buf, "%La", x.value); + fprintf(out, "fmt str: \"%%La\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Le", x.value); + fprintf(out, "fmt str: \"%%Le\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lf", x.value); + fprintf(out, "fmt str: \"%%Lf\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n", x.word[2], x.word[1], x.word[0], buf); + sprintf(buf, "%Lg", x.value); + fprintf(out, "fmt str: \"%%Lg\" exp: %#.4x manthi: %#.8x mantlo: %#.8x\n" + "Shall return: \"nan\" returns: \"%s\"\n\n", x.word[2], x.word[1], x.word[0], buf); + + + fprintf(out, "Testing asprintf.\n"); + fprintf(out, "Code line: strlng = asprintf(&strbuf, \"Pi = %%.15Lf\", 3.1415926535897932384626433832795L);\n"); + strlng = asprintf(&strbuf, "Pi = %.15Lf", 3.1415926535897932384626433832795L); + fprintf(out, "Result: strbuf: \"%s\" strlng: %d\n", strbuf, strlng); + free(strbuf); + + fprintf(out, "Testing asnprintf.\n"); + strbuf = NULL; + fprintf(out, "Code line: strlng = asnprintf(&strbuf, 0, \"Pi = %%.15Lf\", 3.1415926535897932384626433832795L);\n"); + strlng = asnprintf(&strbuf, 0, "Pi = %.15Lf", 3.1415926535897932384626433832795L); + fprintf(out, "Result: strbuf: %s strlng: %d\n", strbuf, strlng); + fprintf(out, "Code line: strlng = asnprintf(&strbuf, 10, \"Pi = %%.15Lf\", 3.1415926535897932384626433832795L);\n"); + strlng = asnprintf(&strbuf, 10, "Pi = %.15Lf", 3.1415926535897932384626433832795L); + fprintf(out, "Result: strbuf: 0x%p strlng: %d\n", strbuf, strlng); + fprintf(out, " strbuf: \"%s\" mallocated buffer length is %zd chars long plus 1 nul char\n\n", strbuf, strlen(strbuf)); + free(strbuf); + + fprintf(out, "Testing flags in combination with Infinity and NaN.\n"); + fprintf(out, "Code line: sprintf(buf, \"%%0*Lf\", 10, 1.0L / 0.0L);\n"); + sprintf(buf, "%0*Lf", 10, 1.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "inf"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%+*Lf\", 10, 1.0L / 0.0L);\n"); + sprintf(buf, "%+*Lf", 10, 1.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "+inf"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%-*Lf\", 10, 1.0L / 0.0L);\n"); + sprintf(buf, "%-*Lf", 10, 1.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "inf "); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%% *Lf\", 10, 1.0L / 0.0L);\n"); + sprintf(buf, "% *Lf", 10, 1.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "inf"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%#*Lf\", 10, 1.0L / 0.0L);\n"); + sprintf(buf, "%#*Lf", 10, 1.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "inf"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%0*Lf\", 10, 0.0L / 0.0L);\n"); + sprintf(buf, "%0*Lf", 10, 0.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "nan"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%+*Lf\", 10, 0.0L / 0.0L);\n"); + sprintf(buf, "%+*Lf", 10, 0.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "nan"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%-*Lf\", 10, 0.0L / 0.0L);\n"); + sprintf(buf, "%-*Lf", 10, 0.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "nan "); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%% *Lf\", 10, 0.0L / 0.0L);\n"); + sprintf(buf, "% *Lf", 10, 0.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "nan"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); + fprintf(out, "Code line: sprintf(buf, \"%%#*Lf\", 10, 0.0L / 0.0L);\n"); + sprintf(buf, "%#*Lf", 10, 0.0L / 0.0L); + asprintf(&strbuf, "%*s", (int)strlen(buf), "nan"); + fprintf(out, "Shall return: <%s> returns: <%s> %s\n", strbuf, buf, strcmp(strbuf, buf) ? "Not OK" : "OK"); + free(strbuf); +} + + +int main(void) +{ + FILE *out; + + out = fopen("printf5.txt", "w"); + if (out == NULL) + { + printf("Can not open test.txt. Test failed.\n"); + return 1; + } + + printf("Testing:\n" + " printf family of functions...\n"); + flags_test(out); + length_modifiers_test(out); + numeric_conversion_specifiers_test(out); + printf_test(out); + fclose(out); + + printf("The test output is in printf5.txt\n"); + return 0; +}