189857Sobrien/* Provide a version of _doprnt in terms of fprintf.
289857Sobrien   Copyright (C) 1998, 1999, 2000, 2001, 2002   Free Software Foundation, Inc.
389857Sobrien   Contributed by Kaveh Ghazi  (ghazi@caip.rutgers.edu)  3/29/98
489857Sobrien
589857SobrienThis program is free software; you can redistribute it and/or modify it
689857Sobrienunder the terms of the GNU General Public License as published by the
789857SobrienFree Software Foundation; either version 2, or (at your option) any
889857Sobrienlater version.
989857Sobrien
1089857SobrienThis program is distributed in the hope that it will be useful,
1189857Sobrienbut WITHOUT ANY WARRANTY; without even the implied warranty of
1289857SobrienMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1389857SobrienGNU General Public License for more details.
1489857Sobrien
1589857SobrienYou should have received a copy of the GNU General Public License
1689857Sobrienalong with this program; if not, write to the Free Software
17218822SdimFoundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
1889857Sobrien
1989857Sobrien#include "config.h"
2089857Sobrien#include "ansidecl.h"
2189857Sobrien#include "safe-ctype.h"
2289857Sobrien
2389857Sobrien#include <stdio.h>
2489857Sobrien#include <stdarg.h>
2589857Sobrien#ifdef HAVE_STRING_H
2689857Sobrien#include <string.h>
2789857Sobrien#endif
2889857Sobrien#ifdef HAVE_STDLIB_H
2989857Sobrien#include <stdlib.h>
3089857Sobrien#endif
3189857Sobrien
3289857Sobrien#undef _doprnt
3389857Sobrien
3489857Sobrien#ifdef HAVE__DOPRNT
3589857Sobrien#define TEST
3689857Sobrien#endif
3789857Sobrien
3889857Sobrien#ifdef TEST /* Make sure to use the internal one.  */
3989857Sobrien#define _doprnt my_doprnt
4089857Sobrien#endif
4189857Sobrien
4289857Sobrien#define COPY_VA_INT \
4389857Sobrien  do { \
4489857Sobrien	 const int value = abs (va_arg (ap, int)); \
4589857Sobrien	 char buf[32]; \
4689857Sobrien	 ptr++; /* Go past the asterisk.  */ \
4789857Sobrien	 *sptr = '\0'; /* NULL terminate sptr.  */ \
4889857Sobrien	 sprintf(buf, "%d", value); \
4989857Sobrien	 strcat(sptr, buf); \
5089857Sobrien	 while (*sptr) sptr++; \
5189857Sobrien     } while (0)
5289857Sobrien
5389857Sobrien#define PRINT_CHAR(CHAR) \
5489857Sobrien  do { \
5589857Sobrien	 putc(CHAR, stream); \
5689857Sobrien	 ptr++; \
5789857Sobrien	 total_printed++; \
5889857Sobrien	 continue; \
5989857Sobrien     } while (0)
6089857Sobrien
6189857Sobrien#define PRINT_TYPE(TYPE) \
6289857Sobrien  do { \
6389857Sobrien	int result; \
6489857Sobrien	TYPE value = va_arg (ap, TYPE); \
6589857Sobrien	*sptr++ = *ptr++; /* Copy the type specifier.  */ \
6689857Sobrien	*sptr = '\0'; /* NULL terminate sptr.  */ \
6789857Sobrien	result = fprintf(stream, specifier, value); \
6889857Sobrien	if (result == -1) \
6989857Sobrien	  return -1; \
7089857Sobrien	else \
7189857Sobrien	  { \
7289857Sobrien	    total_printed += result; \
7389857Sobrien	    continue; \
7489857Sobrien	  } \
7589857Sobrien      } while (0)
7689857Sobrien
7789857Sobrienint
78218822Sdim_doprnt (const char *format, va_list ap, FILE *stream)
7989857Sobrien{
8089857Sobrien  const char * ptr = format;
8189857Sobrien  char specifier[128];
8289857Sobrien  int total_printed = 0;
8389857Sobrien
8489857Sobrien  while (*ptr != '\0')
8589857Sobrien    {
8689857Sobrien      if (*ptr != '%') /* While we have regular characters, print them.  */
8789857Sobrien	PRINT_CHAR(*ptr);
8889857Sobrien      else /* We got a format specifier! */
8989857Sobrien	{
9089857Sobrien	  char * sptr = specifier;
9189857Sobrien	  int wide_width = 0, short_width = 0;
9289857Sobrien
9389857Sobrien	  *sptr++ = *ptr++; /* Copy the % and move forward.  */
9489857Sobrien
9589857Sobrien	  while (strchr ("-+ #0", *ptr)) /* Move past flags.  */
9689857Sobrien	    *sptr++ = *ptr++;
9789857Sobrien
9889857Sobrien	  if (*ptr == '*')
9989857Sobrien	    COPY_VA_INT;
10089857Sobrien	  else
10189857Sobrien	    while (ISDIGIT(*ptr)) /* Handle explicit numeric value.  */
10289857Sobrien	      *sptr++ = *ptr++;
10389857Sobrien
10489857Sobrien	  if (*ptr == '.')
10589857Sobrien	    {
10689857Sobrien	      *sptr++ = *ptr++; /* Copy and go past the period.  */
10789857Sobrien	      if (*ptr == '*')
10889857Sobrien		COPY_VA_INT;
10989857Sobrien	      else
11089857Sobrien		while (ISDIGIT(*ptr)) /* Handle explicit numeric value.  */
11189857Sobrien		  *sptr++ = *ptr++;
11289857Sobrien	    }
11389857Sobrien	  while (strchr ("hlL", *ptr))
11489857Sobrien	    {
11589857Sobrien	      switch (*ptr)
11689857Sobrien		{
11789857Sobrien		case 'h':
11889857Sobrien		  short_width = 1;
11989857Sobrien		  break;
12089857Sobrien		case 'l':
12189857Sobrien		  wide_width++;
12289857Sobrien		  break;
12389857Sobrien		case 'L':
12489857Sobrien		  wide_width = 2;
12589857Sobrien		  break;
12689857Sobrien		default:
12789857Sobrien		  abort();
12889857Sobrien		}
12989857Sobrien	      *sptr++ = *ptr++;
13089857Sobrien	    }
13189857Sobrien
13289857Sobrien	  switch (*ptr)
13389857Sobrien	    {
13489857Sobrien	    case 'd':
13589857Sobrien	    case 'i':
13689857Sobrien	    case 'o':
13789857Sobrien	    case 'u':
13889857Sobrien	    case 'x':
13989857Sobrien	    case 'X':
14089857Sobrien	    case 'c':
14189857Sobrien	      {
14289857Sobrien		/* Short values are promoted to int, so just copy it
14389857Sobrien                   as an int and trust the C library printf to cast it
14489857Sobrien                   to the right width.  */
14589857Sobrien		if (short_width)
14689857Sobrien		  PRINT_TYPE(int);
14789857Sobrien		else
14889857Sobrien		  {
14989857Sobrien		    switch (wide_width)
15089857Sobrien		      {
15189857Sobrien		      case 0:
15289857Sobrien			PRINT_TYPE(int);
15389857Sobrien			break;
15489857Sobrien		      case 1:
15589857Sobrien			PRINT_TYPE(long);
15689857Sobrien			break;
15789857Sobrien		      case 2:
15889857Sobrien		      default:
15989857Sobrien#if defined(__GNUC__) || defined(HAVE_LONG_LONG)
16089857Sobrien			PRINT_TYPE(long long);
16189857Sobrien#else
16289857Sobrien			PRINT_TYPE(long); /* Fake it and hope for the best.  */
16389857Sobrien#endif
16489857Sobrien			break;
16589857Sobrien		      } /* End of switch (wide_width) */
16689857Sobrien		  } /* End of else statement */
16789857Sobrien	      } /* End of integer case */
16889857Sobrien	      break;
16989857Sobrien	    case 'f':
17089857Sobrien	    case 'e':
17189857Sobrien	    case 'E':
17289857Sobrien	    case 'g':
17389857Sobrien	    case 'G':
17489857Sobrien	      {
17589857Sobrien		if (wide_width == 0)
17689857Sobrien		  PRINT_TYPE(double);
17789857Sobrien		else
17889857Sobrien		  {
17989857Sobrien#if defined(__GNUC__) || defined(HAVE_LONG_DOUBLE)
18089857Sobrien		    PRINT_TYPE(long double);
18189857Sobrien#else
18289857Sobrien		    PRINT_TYPE(double); /* Fake it and hope for the best.  */
18389857Sobrien#endif
18489857Sobrien		  }
18589857Sobrien	      }
18689857Sobrien	      break;
18789857Sobrien	    case 's':
18889857Sobrien	      PRINT_TYPE(char *);
18989857Sobrien	      break;
19089857Sobrien	    case 'p':
19189857Sobrien	      PRINT_TYPE(void *);
19289857Sobrien	      break;
19389857Sobrien	    case '%':
19489857Sobrien	      PRINT_CHAR('%');
19589857Sobrien	      break;
19689857Sobrien	    default:
19789857Sobrien	      abort();
19889857Sobrien	    } /* End of switch (*ptr) */
19989857Sobrien	} /* End of else statement */
20089857Sobrien    }
20189857Sobrien
20289857Sobrien  return total_printed;
20389857Sobrien}
20489857Sobrien
20589857Sobrien#ifdef TEST
20689857Sobrien
20789857Sobrien#include <math.h>
20889857Sobrien#ifndef M_PI
20989857Sobrien#define M_PI (3.1415926535897932385)
21089857Sobrien#endif
21189857Sobrien
21289857Sobrien#define RESULT(x) do \
21389857Sobrien{ \
21489857Sobrien    int i = (x); \
21589857Sobrien    printf ("printed %d characters\n", i); \
21689857Sobrien    fflush(stdin); \
21789857Sobrien} while (0)
21889857Sobrien
219218822Sdimstatic int checkit (const char * format, ...) ATTRIBUTE_PRINTF_1;
22089857Sobrien
22189857Sobrienstatic int
222218822Sdimcheckit (const char* format, ...)
22389857Sobrien{
22489857Sobrien  int result;
22589857Sobrien  VA_OPEN (args, format);
22689857Sobrien  VA_FIXEDARG (args, char *, format);
22789857Sobrien
22889857Sobrien  result = _doprnt (format, args, stdout);
22989857Sobrien  VA_CLOSE (args);
23089857Sobrien
23189857Sobrien  return result;
23289857Sobrien}
23389857Sobrien
23489857Sobrienint
235218822Sdimmain (void)
23689857Sobrien{
23789857Sobrien  RESULT(checkit ("<%d>\n", 0x12345678));
23889857Sobrien  RESULT(printf ("<%d>\n", 0x12345678));
23989857Sobrien
24089857Sobrien  RESULT(checkit ("<%200d>\n", 5));
24189857Sobrien  RESULT(printf ("<%200d>\n", 5));
24289857Sobrien
24389857Sobrien  RESULT(checkit ("<%.300d>\n", 6));
24489857Sobrien  RESULT(printf ("<%.300d>\n", 6));
24589857Sobrien
24689857Sobrien  RESULT(checkit ("<%100.150d>\n", 7));
24789857Sobrien  RESULT(printf ("<%100.150d>\n", 7));
24889857Sobrien
24989857Sobrien  RESULT(checkit ("<%s>\n",
25089857Sobrien		  "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
25189857Sobrien777777777777777777333333333333366666666666622222222222777777777777733333"));
25289857Sobrien  RESULT(printf ("<%s>\n",
25389857Sobrien		 "jjjjjjjjjiiiiiiiiiiiiiiioooooooooooooooooppppppppppppaa\n\
25489857Sobrien777777777777777777333333333333366666666666622222222222777777777777733333"));
25589857Sobrien
25689857Sobrien  RESULT(checkit ("<%f><%0+#f>%s%d%s>\n",
25789857Sobrien		  1.0, 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx"));
25889857Sobrien  RESULT(printf ("<%f><%0+#f>%s%d%s>\n",
25989857Sobrien		 1.0, 1.0, "foo", 77, "asdjffffffffffffffiiiiiiiiiiixxxxx"));
26089857Sobrien
26189857Sobrien  RESULT(checkit ("<%4f><%.4f><%%><%4.4f>\n", M_PI, M_PI, M_PI));
26289857Sobrien  RESULT(printf ("<%4f><%.4f><%%><%4.4f>\n", M_PI, M_PI, M_PI));
26389857Sobrien
26489857Sobrien  RESULT(checkit ("<%*f><%.*f><%%><%*.*f>\n", 3, M_PI, 3, M_PI, 3, 3, M_PI));
26589857Sobrien  RESULT(printf ("<%*f><%.*f><%%><%*.*f>\n", 3, M_PI, 3, M_PI, 3, 3, M_PI));
26689857Sobrien
26789857Sobrien  RESULT(checkit ("<%d><%i><%o><%u><%x><%X><%c>\n",
26889857Sobrien		  75, 75, 75, 75, 75, 75, 75));
26989857Sobrien  RESULT(printf ("<%d><%i><%o><%u><%x><%X><%c>\n",
27089857Sobrien		 75, 75, 75, 75, 75, 75, 75));
27189857Sobrien
27289857Sobrien  RESULT(checkit ("<%d><%i><%o><%u><%x><%X><%c>\n",
27389857Sobrien		  75, 75, 75, 75, 75, 75, 75));
27489857Sobrien  RESULT(printf ("<%d><%i><%o><%u><%x><%X><%c>\n",
27589857Sobrien		 75, 75, 75, 75, 75, 75, 75));
27689857Sobrien
27789857Sobrien  RESULT(checkit ("Testing (hd) short: <%d><%ld><%hd><%hd><%d>\n", 123, (long)234, 345, 123456789, 456));
27889857Sobrien  RESULT(printf ("Testing (hd) short: <%d><%ld><%hd><%hd><%d>\n", 123, (long)234, 345, 123456789, 456));
27989857Sobrien
28089857Sobrien#if defined(__GNUC__) || defined (HAVE_LONG_LONG)
28189857Sobrien  RESULT(checkit ("Testing (lld) long long: <%d><%lld><%d>\n", 123, 234234234234234234LL, 345));
28289857Sobrien  RESULT(printf ("Testing (lld) long long: <%d><%lld><%d>\n", 123, 234234234234234234LL, 345));
28389857Sobrien  RESULT(checkit ("Testing (Ld) long long: <%d><%Ld><%d>\n", 123, 234234234234234234LL, 345));
28489857Sobrien  RESULT(printf ("Testing (Ld) long long: <%d><%Ld><%d>\n", 123, 234234234234234234LL, 345));
28589857Sobrien#endif
28689857Sobrien
28789857Sobrien#if defined(__GNUC__) || defined (HAVE_LONG_DOUBLE)
28889857Sobrien  RESULT(checkit ("Testing (Lf) long double: <%.20f><%.20Lf><%0+#.20f>\n",
28989857Sobrien		  1.23456, 1.234567890123456789L, 1.23456));
29089857Sobrien  RESULT(printf ("Testing (Lf) long double: <%.20f><%.20Lf><%0+#.20f>\n",
29189857Sobrien		 1.23456, 1.234567890123456789L, 1.23456));
29289857Sobrien#endif
29389857Sobrien
29489857Sobrien  return 0;
29589857Sobrien}
29689857Sobrien#endif /* TEST */
297