
Re: our conversation on Jabber about formatting data for the repr tree, here is some code I appropriated from Apache's APR lib, which can give us total control over formatting output strings.
With Mental and Bryce's work in this area, these files might not be necessary, but then again they might be useful, so here they are. One very nice function for the repr tree would be:
sp_repr_printf(repr, key, "format", args...)
Would relieve the need for a lot of temporary buffers all over the place.
Bob
/*######################################################################## ## $Id$ ########################################################################*/
/* * Print formatter. This file's purpose is to * allow Inkscape developers to have complete * control over formatted strings. This can be * tweaked to suit. * * This file was basically copied from Apache's * apr_snprintf.c, but with extensive modifications * to ease embedding in a GLib project. Their license * is below. * * Copyright (C) 1999-2003 Authors * * Released under GNU GPL, read the file 'COPYING' for more information * */
/* ==================================================================== * The Apache Software License, Version 1.1 * * Copyright (c) 2000-2003 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, * if any, must include the following acknowledgment: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, * if and wherever such third-party acknowledgments normally appear. * * 4. The names "Apache" and "Apache Software Foundation" must * not be used to endorse or promote products derived from this * software without prior written permission. For written * permission, please contact apache@...199... * * 5. Products derived from this software may not be called "Apache", * nor may "Apache" appear in their name, without prior written * permission of the Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * http://www.apache.org/. * * The ink_vsnprintf/ink_snprintf functions are based on, and used with the * permission of, the SIO stdio-replacement strx_* functions by Panos * Tsirigotis <panos@...200...> for xinetd. */
#include <math.h> #include <ctype.h> #include <string.h>
#include <glib.h>
#include "ink-snprintf.h"
/*#################################### # D E F I N I T I O N S ####################################*/
/** * Structure used by the variable-formatter routines. */ struct ink_vformatter_buff_t { gchar *curpos;/** The current position */ gchar *endpos;/** The end position of the format string */ }; typedef struct ink_vformatter_buff_t ink_vformatter_buff_t;
typedef enum { NO = 0, YES = 1 } boolean_e;
#ifndef FALSE #define FALSE 0 #endif #ifndef TRUE #define TRUE 1 #endif #define NUL '\0'
#define S_NULL "(null)" #define S_NULL_LEN 6
#define FLOAT_DIGITS 6 #define EXPONENT_LENGTH 10
/* * NUM_BUF_SIZE is the size of the buffer used for arithmetic conversions * * NOTICE: this is a magic number; do not decrease it */ #define NUM_BUF_SIZE 512
/* * cvt.c - IEEE floating point formatting routines for FreeBSD * from GNU libc-4.6.27. Modified to be thread safe. */
/* * ink_ecvt converts to decimal * the number of digits is specified by ndigit * decpt is set to the position of the decimal point * sign is set to 0 for positive, 1 for negative */
#define NDIG 80
/*#################################### # M A C R O S ####################################*/
/* * The INS_CHAR macro inserts a character in the buffer and writes * the buffer back to disk if necessary * It uses the char pointers sp and bep: * sp points to the next available character in the buffer * bep points to the end-of-buffer+1 * While using this macro, note that the nextb pointer is NOT updated. * * NOTE: Evaluation of the c argument should not have any side-effects */ #define INS_CHAR(c, sp, bep, cc) \ { \ if (sp) { \ if (sp >= bep) { \ vbuff->curpos = sp; \ if (flush_func(vbuff)) \ return -1; \ sp = vbuff->curpos; \ bep = vbuff->endpos; \ } \ *sp++ = (c); \ } \ cc++; \ }
#define NUM(c) (c - '0')
#define STR_TO_DEC(str, num) \ num = NUM(*str++); \ while (g_ascii_isdigit(*str)) \ { \ num *= 10 ; \ num += NUM(*str++); \ }
/* * This macro does zero padding so that the precision * requirement is satisfied. The padding is done by * adding '0's to the left of the string that is going * to be printed. We don't allow precision to be large * enough that we continue past the start of s. * * NOTE: this makes use of the magic info that s is * always based on num_buf with a size of NUM_BUF_SIZE. */ #define FIX_PRECISION(adjust, precision, s, s_len) \ if (adjust) { \ gint p = precision < NUM_BUF_SIZE - 1 ? \ precision : NUM_BUF_SIZE - 1; \ while (s_len < p) { \ *--s = '0'; \ s_len++; \ } \ }
/* * Macro that does padding. The padding is done by printing * the character ch. */ #define PAD(width, len, ch) \ do \ { \ INS_CHAR(ch, sp, bep, cc); \ width--; \ } \ while (width > len)
/* * Prefix the character ch to the string str * Increase length * Set the has_prefix flag */ #define PREFIX(str, length, ch) \ *--str = ch; \ length++; \ has_prefix=YES;
/*#################################### # CVT - float-to-ascii conversion ####################################*/
/* buf must have at least NDIG bytes */ static gchar * ink_cvt(gdouble arg, gint ndigits, gint *decpt, gint *sign, gint eflag, gchar *buf) { gint r2; gdouble fi, fj; gchar *p, *p1;
if (ndigits >= NDIG - 1) ndigits = NDIG - 2; r2 = 0; *sign = 0; p = &buf[0]; if (arg < 0) { *sign = 1; arg = -arg; } arg = modf(arg, &fi); p1 = &buf[NDIG]; /* * Do integer part */ if (fi != 0) { p1 = &buf[NDIG]; while (p1 > &buf[0] && fi != 0) { fj = modf(fi / 10, &fi); *--p1 = (int) ((fj + .03) * 10) + '0'; r2++; } while (p1 < &buf[NDIG]) *p++ = *p1++; } else if (arg > 0) { while ((fj = arg * 10) < 1) { arg = fj; r2--; } } p1 = &buf[ndigits]; if (eflag == 0) p1 += r2; *decpt = r2; if (p1 < &buf[0]) { buf[0] = '\0'; return (buf); } while (p <= p1 && p < &buf[NDIG]) { arg *= 10; arg = modf(arg, &fj); *p++ = (int) fj + '0'; } if (p1 >= &buf[NDIG]) { buf[NDIG - 1] = '\0'; return (buf); } p = p1; *p1 += 5; while (*p1 > '9') { *p1 = '0'; if (p1 > buf) ++ * --p1; else { *p1 = '1'; (*decpt)++; if (eflag == 0) { if (p > buf) *p = '0'; p++; } } } *p = '\0'; return (buf); }
static gchar * ink_ecvt(gdouble arg, gint ndigits, gint *decpt, gint *sign, gchar *buf) { return (ink_cvt(arg, ndigits, decpt, sign, 1, buf)); }
static gchar * ink_fcvt(gdouble arg, gint ndigits, gint *decpt, gint *sign, gchar *buf) { return (ink_cvt(arg, ndigits, decpt, sign, 0, buf)); }
/* * ink_gcvt - Floating output conversion to * minimal length string */
static gchar * ink_gcvt(gdouble number, gint ndigit, gchar *buf, boolean_e altform) { gint sign, decpt; gchar *p1, *p2; gint i; gchar buf1[NDIG];
p1 = ink_ecvt(number, ndigit, &decpt, &sign, buf1); p2 = buf; if (sign) *p2++ = '-'; for (i = ndigit - 1; i > 0 && p1[i] == '0'; i--) ndigit--; if ((decpt >= 0 && decpt - ndigit > 4) || (decpt < 0 && decpt < -3)) { /* use E-style */ decpt--; *p2++ = *p1++; *p2++ = '.'; for (i = 1; i < ndigit; i++) *p2++ = *p1++; *p2++ = 'e'; if (decpt < 0) { decpt = -decpt; *p2++ = '-'; } else *p2++ = '+'; if (decpt / 100 > 0) *p2++ = decpt / 100 + '0'; if (decpt / 10 > 0) *p2++ = (decpt % 100) / 10 + '0'; *p2++ = decpt % 10 + '0'; } else { if (decpt <= 0) { if (*p1 != '0') *p2++ = '.'; while (decpt < 0) { decpt++; *p2++ = '0'; } } for (i = 1; i <= ndigit; i++) { *p2++ = *p1++; if (i == decpt) *p2++ = '.'; } if (ndigit < decpt) { while (ndigit++ < decpt) *p2++ = '0'; *p2++ = '.'; } } if (p2[-1] == '.' && !altform) p2--; *p2 = '\0'; return (buf); }
/* * Convert num to its decimal format. * Return value: * - a pointer to a string containing the number (no sign) * - len contains the length of the string * - is_negative is set to TRUE or FALSE depending on the sign * of the number (always set to FALSE if is_unsigned is TRUE) * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) * * Note: we have 2 versions. One is used when we need to use quads * (conv_10_quad), the other when we don't (conv_10). We're assuming the * latter is faster. */ static gchar * conv_10(gint32 num, gboolean is_unsigned, gboolean *is_negative, gchar *buf_end, gint *len) { gchar *p = buf_end; guint32 magnitude;
if (is_unsigned) { magnitude = (guint32) num; *is_negative = FALSE; } else { *is_negative = (num < 0);
/* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if (*is_negative) { gint32 t = num + 1;
magnitude = ((guint32) -t) + 1; } else magnitude = (guint32) num; }
/* * We use a do-while loop so that we write at least 1 digit */ do { guint32 new_magnitude = magnitude / 10;
*--p = (gchar) (magnitude - new_magnitude * 10 + '0'); magnitude = new_magnitude; } while (magnitude);
*len = buf_end - p; return (p); }
static gchar * conv_10_quad(gint64 num, gboolean is_unsigned, gboolean *is_negative, gchar *buf_end, gint *len) { gchar *p = buf_end; guint64 magnitude;
/* * We see if we can use the faster non-quad version by checking the * number against the largest long value it can be. If <=, we * punt to the quicker version. */ if ( (num <= ULONG_MAX && is_unsigned) || (num <= LONG_MAX && !is_unsigned) ) return(conv_10( (gint32)num, is_unsigned, is_negative, buf_end, len));
if (is_unsigned) { magnitude = (guint64) num; *is_negative = FALSE; } else { *is_negative = (num < 0);
/* * On a 2's complement machine, negating the most negative integer * results in a number that cannot be represented as a signed integer. * Here is what we do to obtain the number's magnitude: * a. add 1 to the number * b. negate it (becomes positive) * c. convert it to unsigned * d. add 1 */ if (*is_negative) { gint64 t = num + 1;
magnitude = ((guint64) -t) + 1; } else magnitude = (guint64) num; }
/* * We use a do-while loop so that we write at least 1 digit */ do { guint64 new_magnitude = magnitude / 10;
*--p = (gchar) (magnitude - new_magnitude * 10 + '0'); magnitude = new_magnitude; } while (magnitude);
*len = buf_end - p; return (p); }
/* * Convert a floating point number to a string formats 'f', 'e' or 'E'. * The result is placed in buf, and len denotes the length of the string * The sign is returned in the is_negative argument (and is not placed * in buf). */ static gchar * conv_fp(gchar format, gdouble num, boolean_e add_dp, gint precision, gboolean *is_negative, gchar *buf, gint *len) { gchar *s = buf; gchar *p; gint decimal_point; gchar buf1[NDIG];
if (format == 'f') p = ink_fcvt(num, precision, &decimal_point, is_negative, buf1); else /* either e or E format */ p = ink_ecvt(num, precision + 1, &decimal_point, is_negative, buf1);
/* * Check for Infinity and NaN */ if (g_ascii_isalpha(*p)) { *len = strlen(p); memcpy(buf, p, *len + 1); *is_negative = FALSE; return (buf); }
if (format == 'f') { if (decimal_point <= 0) { *s++ = '0'; if (precision > 0) { *s++ = '.'; while (decimal_point++ < 0) *s++ = '0'; } else if (add_dp) *s++ = '.'; } else { while (decimal_point-- > 0) *s++ = *p++; if (precision > 0 || add_dp) *s++ = '.'; } } else { *s++ = *p++; if (precision > 0 || add_dp) *s++ = '.'; }
/* * copy the rest of p, the NUL is NOT copied */ while (*p) *s++ = *p++;
if (format != 'f') { gchar temp[EXPONENT_LENGTH]; /* for exponent conversion */ gint t_len; gboolean exponent_is_negative;
*s++ = format; /* either e or E */ decimal_point--; if (decimal_point != 0) { p = conv_10((gint32) decimal_point, FALSE, &exponent_is_negative, &temp[EXPONENT_LENGTH], &t_len); *s++ = exponent_is_negative ? '-' : '+';
/* * Make sure the exponent has at least 2 digits */ if (t_len == 1) *s++ = '0'; while (t_len--) *s++ = *p++; } else { *s++ = '+'; *s++ = '0'; *s++ = '0'; } }
*len = s - buf; return (buf); }
/* * Convert num to a base X number where X is a power of 2. nbits determines X. * For example, if nbits is 3, we do base 8 conversion * Return value: * a pointer to a string containing the number * * The caller provides a buffer for the string: that is the buf_end argument * which is a pointer to the END of the buffer + 1 (i.e. if the buffer * is declared as buf[ 100 ], buf_end should be &buf[ 100 ]) * * As with conv_10, we have a faster version which is used when * the number isn't quad size. */ static gchar * conv_p2(guint32 num, gint nbits, gchar format, gchar *buf_end, gint *len) { gint mask = (1 << nbits) - 1; gchar *p = buf_end; const gchar low_digits[] = "0123456789abcdef"; const gchar upper_digits[] = "0123456789ABCDEF"; const gchar *digits = (format == 'X') ? upper_digits : low_digits;
do { *--p = digits[num & mask]; num >>= nbits; } while (num);
*len = buf_end - p; return (p); }
static gchar * conv_p2_quad(guint64 num, gint nbits, gchar format, gchar *buf_end, gint *len) { gint mask = (1 << nbits) - 1; gchar *p = buf_end; const gchar low_digits[] = "0123456789abcdef"; const gchar upper_digits[] = "0123456789ABCDEF"; const gchar *digits = (format == 'X') ? upper_digits : low_digits;
if (num <= ULONG_MAX) return(conv_p2((guint32)num, nbits, format, buf_end, len));
do { *--p = digits[num & mask]; num >>= nbits; } while (num);
*len = buf_end - p; return (p); }
/*#################################### # F O R M A T T I N G ####################################*/ /* * Do format conversion placing the output in buffer */ static gint ink_vformatter(gint (*flush_func)(ink_vformatter_buff_t *), ink_vformatter_buff_t *vbuff, const gchar *fmt, va_list ap) { gchar *sp; gchar *bep; gint cc = 0; gint i;
gchar *s = NULL; gchar *q; gint s_len;
gint min_width = 0; gint precision = 0; enum { LEFT, RIGHT } adjust; gchar pad_char; gchar prefix_char;
gdouble fp_num; gint64 i_quad = (gint64) 0; guint64 ui_quad; gint32 i_num = (gint32) 0; guint32 ui_num;
gchar num_buf[NUM_BUF_SIZE]; gchar char_buf[2]; /* for printing %% and %<unknown> */
enum var_type_enum { IS_QUAD, IS_LONG, IS_SHORT, IS_INT }; enum var_type_enum var_type = IS_INT;
/* * Flag variables */ boolean_e alternate_form; boolean_e print_sign; boolean_e print_blank; boolean_e adjust_precision; boolean_e adjust_width; gboolean is_negative;
sp = vbuff->curpos; bep = vbuff->endpos;
while (*fmt) { if (*fmt != '%') { INS_CHAR(*fmt, sp, bep, cc); } else { /* * Default variable settings */ boolean_e print_something = YES; adjust = RIGHT; alternate_form = print_sign = print_blank = NO; pad_char = ' '; prefix_char = NUL;
fmt++;
/* * Try to avoid checking for flags, width or precision */ if (!g_ascii_islower(*fmt)) { /* * Recognize flags: -, #, BLANK, + */ for (;; fmt++) { if (*fmt == '-') adjust = LEFT; else if (*fmt == '+') print_sign = YES; else if (*fmt == '#') alternate_form = YES; else if (*fmt == ' ') print_blank = YES; else if (*fmt == '0') pad_char = '0'; else break; }
/* * Check if a width was specified */ if (g_ascii_isdigit(*fmt)) { STR_TO_DEC(fmt, min_width); adjust_width = YES; } else if (*fmt == '*') { min_width = va_arg(ap, int); fmt++; adjust_width = YES; if (min_width < 0) { adjust = LEFT; min_width = -min_width; } } else adjust_width = NO;
/* * Check if a precision was specified */ if (*fmt == '.') { adjust_precision = YES; fmt++; if ( g_ascii_isdigit (*fmt)) { STR_TO_DEC(fmt, precision); } else if (*fmt == '*') { precision = va_arg(ap, int); fmt++; if (precision < 0) precision = 0; } else precision = 0; } else adjust_precision = NO; } else adjust_precision = adjust_width = NO;
/* * Modifier check */ if (*fmt == 'q') { var_type = IS_QUAD; fmt++; } else if (*fmt == 'l') { var_type = IS_LONG; fmt++; } else if (*fmt == 'h') { var_type = IS_SHORT; fmt++; } else { var_type = IS_INT; }
/* * Argument extraction and printing. * First we determine the argument type. * Then, we convert the argument to a string. * On exit from the switch, s points to the string that * must be printed, s_len has the length of the string * The precision requirements, if any, are reflected in s_len. * * NOTE: pad_char may be set to '0' because of the 0 flag. * It is reset to ' ' by non-numeric formats */ switch (*fmt) { case 'u': if (var_type == IS_QUAD) { i_quad = va_arg(ap, guint64); s = conv_10_quad(i_quad, 1, &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); } else { if (var_type == IS_LONG) i_num = (gint32) va_arg(ap, guint32); else if (var_type == IS_SHORT) i_num = (gint32) (unsigned short) va_arg(ap, unsigned int); else i_num = (gint32) va_arg(ap, unsigned int); s = conv_10(i_num, 1, &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); } FIX_PRECISION(adjust_precision, precision, s, s_len); break;
case 'd': case 'i': if (var_type == IS_QUAD) { i_quad = va_arg(ap, gint64); s = conv_10_quad(i_quad, 0, &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); } else { if (var_type == IS_LONG) i_num = (gint32) va_arg(ap, gint32); else if (var_type == IS_SHORT) i_num = (gint32) (short) va_arg(ap, int); else i_num = (gint32) va_arg(ap, int); s = conv_10(i_num, 0, &is_negative, &num_buf[NUM_BUF_SIZE], &s_len); } FIX_PRECISION(adjust_precision, precision, s, s_len);
if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; break;
case 'o': if (var_type == IS_QUAD) { ui_quad = va_arg(ap, guint64); s = conv_p2_quad(ui_quad, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); } else { if (var_type == IS_LONG) ui_num = (guint32) va_arg(ap, guint32); else if (var_type == IS_SHORT) ui_num = (guint32) (unsigned short) va_arg(ap, unsigned int); else ui_num = (guint32) va_arg(ap, unsigned int); s = conv_p2(ui_num, 3, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); } FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && *s != '0') { *--s = '0'; s_len++; } break;
case 'x': case 'X': if (var_type == IS_QUAD) { ui_quad = va_arg(ap, guint64); s = conv_p2_quad(ui_quad, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); } else { if (var_type == IS_LONG) ui_num = (guint32) va_arg(ap, guint32); else if (var_type == IS_SHORT) ui_num = (guint32) (unsigned short) va_arg(ap, unsigned int); else ui_num = (guint32) va_arg(ap, unsigned int); s = conv_p2(ui_num, 4, *fmt, &num_buf[NUM_BUF_SIZE], &s_len); } FIX_PRECISION(adjust_precision, precision, s, s_len); if (alternate_form && i_num != 0) { *--s = *fmt; /* 'x' or 'X' */ *--s = '0'; s_len += 2; } break;
case 's': s = va_arg(ap, gchar *); if (s != NULL) { if (!adjust_precision) { s_len = strlen(s); } else { /* From the C library standard in section 7.9.6.1: * ...if the precision is specified, no more then * that many characters are written. If the * precision is not specified or is greater * than the size of the array, the array shall * contain a null character. * * My reading is is precision is specified and * is less then or equal to the size of the * array, no null character is required. So * we can't do a strlen. * * This figures out the length of the string * up to the precision. Once it's long enough * for the specified precision, we don't care * anymore. * * NOTE: you must do the length comparison * before the check for the null character. * Otherwise, you'll check one beyond the * last valid character. */ const gchar *walk;
for (walk = s, s_len = 0; (s_len < precision) && (*walk != '\0'); ++walk, ++s_len); } } else { s = S_NULL; s_len = S_NULL_LEN; } pad_char = ' '; break;
case 'f': case 'e': case 'E': fp_num = va_arg(ap, double); /* * We use &num_buf[ 1 ], so that we have room for the sign */ s = NULL; #ifdef HAVE_ISNAN if (isnan(fp_num)) { s = "nan"; s_len = 3; } #endif #ifdef HAVE_ISINF if (!s && isinf(fp_num)) { s = "inf"; s_len = 3; } #endif if (!s) { s = conv_fp(*fmt, fp_num, alternate_form, (adjust_precision == NO) ? FLOAT_DIGITS : precision, &is_negative, &num_buf[1], &s_len); if (is_negative) prefix_char = '-'; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' '; } break;
case 'g': case 'G': if (adjust_precision == NO) precision = FLOAT_DIGITS; else if (precision == 0) precision = 1; /* * * We use &num_buf[ 1 ], so that we have room for the sign */ s = ink_gcvt(va_arg(ap, double), precision, &num_buf[1], alternate_form); if (*s == '-') prefix_char = *s++; else if (print_sign) prefix_char = '+'; else if (print_blank) prefix_char = ' ';
s_len = strlen(s);
if (alternate_form && (q = strchr(s, '.')) == NULL) { s[s_len++] = '.'; s[s_len] = '\0'; /* delimit for following strchr() */ } if (*fmt == 'G' && (q = strchr(s, 'e')) != NULL) *q = 'E'; break;
case 'c': char_buf[0] = (char) (va_arg(ap, int)); s = &char_buf[0]; s_len = 1; pad_char = ' '; break;
case '%': char_buf[0] = '%'; s = &char_buf[0]; s_len = 1; pad_char = ' '; break;
case 'n': if (var_type == IS_QUAD) *(va_arg(ap, gint64 *)) = cc; else if (var_type == IS_LONG) *(va_arg(ap, long *)) = cc; else if (var_type == IS_SHORT) *(va_arg(ap, short *)) = cc; else *(va_arg(ap, int *)) = cc; print_something = NO; break;
/* * This is where we extend the printf format, with a second * type specifier */ case 'p': switch(*++fmt) { /* * If the pointer size is equal to or smaller than the size * of the largest unsigned int, we convert the pointer to a * hex number, otherwise we print "%p" to indicate that we * don't handle "%p". */ case 'p': #ifdef VOID_P_IS_QUAD if (sizeof(void *) <= sizeof(guint64)) { ui_quad = (guint64) va_arg(ap, void *); s = conv_p2_quad(ui_quad, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); } #else if (sizeof(void *) <= sizeof(guint32)) { ui_num = (guint32) va_arg(ap, void *); s = conv_p2(ui_num, 4, 'x', &num_buf[NUM_BUF_SIZE], &s_len); } #endif else { s = "%p"; s_len = 2; prefix_char = NUL; } pad_char = ' '; break;
case 'T': char_buf[0] = '0'; s = &char_buf[0]; s_len = 1; pad_char = ' '; break;
case NUL: /* if %p ends the string, oh well ignore it */ continue;
default: s = "bogus %p"; s_len = 8; prefix_char = NUL; (void)va_arg(ap, void *); /* skip the bogus argument on the stack */ break; } break;
case NUL: /* * The last character of the format string was %. * We ignore it. */ continue;
/* * The default case is for unrecognized %'s. * We print %<char> to help the user identify what * option is not understood. * This is also useful in case the user wants to pass * the output of format_converter to another function * that understands some other %<char> (like syslog). * Note that we can't point s inside fmt because the * unknown <char> could be preceded by width etc. */ default: char_buf[0] = '%'; char_buf[1] = *fmt; s = char_buf; s_len = 2; pad_char = ' '; break; }
if (prefix_char != NUL && s != S_NULL && s != char_buf) { *--s = prefix_char; s_len++; }
if (adjust_width && adjust == RIGHT && min_width > s_len) { if (pad_char == '0' && prefix_char != NUL) { INS_CHAR(*s, sp, bep, cc); s++; s_len--; min_width--; } PAD(min_width, s_len, pad_char); }
/* * Print the string s. */ if (print_something == YES) { for (i = s_len; i != 0; i--) { INS_CHAR(*s, sp, bep, cc); s++; } }
if (adjust_width && adjust == LEFT && min_width > s_len) PAD(min_width, s_len, pad_char); } fmt++; } vbuff->curpos = sp;
return cc; }
static int snprintf_flush(ink_vformatter_buff_t *vbuff) { /* if the buffer fills we have to abort immediately, there is no way * to "flush" an ink_snprintf... there's nowhere to flush it to. */ return -1; }
gint ink_snprintf(gchar *buf, gint len, const gchar *format, ...) { gint cc; va_list ap; ink_vformatter_buff_t vbuff;
if (len == 0) { /* NOTE: This is a special case; we just want to return the number * of chars that would be written (minus \0) if the buffer * size was infinite. We leverage the fact that INS_CHAR * just does actual inserts iff the buffer pointer is non-NULL. * In this case, we don't care what buf is; it can be NULL, since * we don't touch it at all. */ vbuff.curpos = NULL; vbuff.endpos = NULL; } else { /* save one byte for nul terminator */ vbuff.curpos = buf; vbuff.endpos = buf + len - 1; } va_start(ap, format); cc = ink_vformatter(snprintf_flush, &vbuff, format, ap); va_end(ap); if (len != 0) { *vbuff.curpos = '\0'; } return (cc == -1) ? (gint)len : cc; }
gint ink_vsnprintf(gchar *buf, gint len, const gchar *format, va_list ap) { gint cc; ink_vformatter_buff_t vbuff;
if (len == 0) { /* See above note */ vbuff.curpos = NULL; vbuff.endpos = NULL; } else { /* save one byte for nul terminator */ vbuff.curpos = buf; vbuff.endpos = buf + len - 1; } cc = ink_vformatter(snprintf_flush, &vbuff, format, ap); if (len != 0) { *vbuff.curpos = '\0'; } return (cc == -1) ? (gint)len : cc; }
#ifndef __INK_SNPRINTF_H__ #define __INK_SNPRINTF_H__ /*######################################################################## ## $Id$ ########################################################################*/
/* * Print formatter. This file's purpose is to * allow Inkscape developers to have complete * control over formatted strings. This can be * tweaked to suit. * * Copyright (C) 1999-2003 Authors * * Released under GNU GPL, read the file 'COPYING' for more information */
#include <glib.h> #include <stdarg.h>
/** * Create a formatted string with a given format string * and a standard variable argument list. */ gint ink_snprintf(gchar *buf, gint len, const gchar *format, ...);
/** * Create a formatted string with a given format string * and argument list. */ gint ink_vsnprintf(gchar *buf, gint len, const gchar *format, va_list ap);
#endif /* __INK_SNPRINTF_H__ */
participants (1)
-
Bob Jamison