Skip to content
mprintf.c 28.1 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
/****************************************************************************
 *
 * $Id$
 *
 *************************************************************************
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
 *
Daniel Stenberg's avatar
Daniel Stenberg committed
 * Purpose:
 *  A merge of Bjorn Reese's format() function and Daniel's dsprintf()
 *  1.0. A full blooded printf() clone with full support for <num>$
 *  everywhere (parameters, widths and precisions) including variabled
 *  sized parameters (like doubles, long longs, long doubles and even
 *  void * in 64-bit architectures).
 *
 * Current restrictions:
 * - Max 128 parameters
 * - No 'long double' support.
 *
 *************************************************************************
 *
 *
 * 1998/01/10  (v2.8)
 *   Daniel
 *   - Updated version number.
 *   - Corrected a static non-zero prefixed width problem.
 *
 * 1998/11/17 - Daniel
 *   Added daprintf() and dvaprintf() for allocated printf() and vprintf().
 *   They return an allocated buffer with the result inside. The result must
 *   be free()ed!
 *
 * 1998/08/23 - breese
 *
 *   Converted all non-printable (and non-whitespace) characters into
 *   their decimal ASCII value preceeded by a '\' character
 *   (this only applies to snprintf family so far)
 *
 *   Added %S (which is the same as %#s)
 *
 * 1998/05/05 (v2.7)
 *
 *   Fixed precision and width qualifiers (%.*s)
 *
 *   Added support for snprintf()
 *
 *   Quoting (%#s) is disabled for the (nil) pointer
 *
 * 1997/06/09 (v2.6)
 *
 *   %#s means that the string will be quoted with "
 *   (I was getting tired of writing \"%s\" all the time)
 *
 *   [ERR] for strings changed to (nil)
 *
 * v2.5
 * - Added C++ support
 * - Prepended all internal functions with dprintf_
 * - Defined the booleans
 *
 * v2.4
 * - Added dvsprintf(), dvfprintf() and dvprintf().
 * - Made the formatting function available with the name _formatf() to enable
 *   other *printf()-inspired functions. (I considered adding a dmsprintf()
 *   that works like sprintf() but allocates the destination string and
 *   possibly enlarges it itself, but things like that should be done with the
 *   new _formatf() instead.)
 *
 * v2.3
 * - Small modifications to make it compile nicely at both Daniel's and
 *   Bjorn's place.
 *
 * v2.2
 * - Made it work with text to the right of the last %!
 * - Introduced dprintf(), dsprintf() and dfprintf().
 * - Float/double support enabled. This system is currently using the ordinary
 *   sprintf() function. NOTE that positional parameters, widths and precisions
 *   will still work like it should since the d-system takes care of that and
 *   passes that information re-formatted to the old sprintf().
 *
 * v2.1
 * - Fixed space padding (i.e %d was extra padded previously)
 * - long long output is supported
 * - alternate output is done correct like in %#08x
 *
 ****************************************************************************/

static const char rcsid[] = "@(#)$Id$";

/*
 * To test:
 *
 * Use WIDTH, PRECISION and NUMBERED ARGUMENT combined.
 */

#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>
#include <string.h>

/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
#define MAX_PARAMETERS 128 /* lame static limit */

#undef TRUE
#undef FALSE
#undef BOOL
#ifdef __cplusplus
# define TRUE true
# define FALSE false
# define BOOL bool
#else
# define TRUE  ((char)(1 == 1))
# define FALSE ((char)(0 == 1))
# define BOOL char
#endif


/* Lower-case digits.  */
static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";

/* Upper-case digits.  */
static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";

#define	OUTCHAR(x) done+=(stream(x, (FILE *)data)==-1?0:1)

/* Data type to read from the arglist */
typedef enum  {
  FORMAT_UNKNOWN = 0,
  FORMAT_STRING,
  FORMAT_PTR,
  FORMAT_INT,
  FORMAT_INTPTR,
  FORMAT_LONG,
  FORMAT_LONGLONG,
  FORMAT_DOUBLE,
  FORMAT_LONGDOUBLE,
  FORMAT_WIDTH /* For internal use */
} FormatType;

/* convertion and display flags */
enum {
  FLAGS_NEW        = 0,
  FLAGS_SPACE      = 1<<0,
  FLAGS_SHOWSIGN   = 1<<1,
  FLAGS_LEFT       = 1<<2,
  FLAGS_ALT        = 1<<3,
  FLAGS_SHORT      = 1<<4,
  FLAGS_LONG       = 1<<5,
  FLAGS_LONGLONG   = 1<<6,
  FLAGS_LONGDOUBLE = 1<<7,
  FLAGS_PAD_NIL    = 1<<8,
  FLAGS_UNSIGNED   = 1<<9,
  FLAGS_OCTAL      = 1<<10,
  FLAGS_HEX        = 1<<11,
  FLAGS_UPPER      = 1<<12,
  FLAGS_WIDTH      = 1<<13, /* '*' or '*<num>$' used */
  FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
  FLAGS_PREC       = 1<<15, /* precision was specified */
  FLAGS_PRECPARAM  = 1<<16, /* precision PARAMETER was specified */
  FLAGS_CHAR       = 1<<17, /* %c story */
  FLAGS_FLOATE     = 1<<18, /* %e or %E */
  FLAGS_FLOATG     = 1<<19  /* %g or %G */
};

typedef struct {
  FormatType type;
  int flags;
  int width;     /* width OR width parameter number */
  int precision; /* precision OR precision parameter number */
  union {
    char *str;
    void *ptr;
    long num;
#if SIZEOF_LONG_LONG /* if this is non-zero */
    long long lnum;
#endif
    double dnum;
#if SIZEOF_LONG_DOUBLE
    long double ldnum;
#endif
  } data;
} va_stack_t;

struct nsprintf {
  char *buffer;
  size_t length;
Loading
Loading full blame…