📄 vasnprintf.c
字号:
/* vsprintf with automatic memory allocation. Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#ifndef _WIN32/* Tell glibc's <stdio.h> to provide a prototype for snprintf(). This must come before <config.h> because <config.h> may include <features.h>, and once <features.h> has been included, it's too late. */#ifndef _GNU_SOURCE# define _GNU_SOURCE 1#endif#endif#ifdef HAVE_CONFIG_H# include <config.h>#endif#include "glib/galloca.h"#include "g-gnulib.h"/* Specification. */#include "vasnprintf.h"#include <stdio.h> /* snprintf(), sprintf() */#include <stdlib.h> /* abort(), malloc(), realloc(), free() */#include <string.h> /* memcpy(), strlen() */#include <errno.h> /* errno */#include <limits.h> /* CHAR_BIT */#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */#include "printf-parse.h"#ifdef HAVE_WCHAR_T# ifdef HAVE_WCSLEN# define local_wcslen wcslen# else /* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid a dependency towards this library, here is a local substitute. Define this substitute only once, even if this file is included twice in the same compilation unit. */# ifndef local_wcslen_defined# define local_wcslen_defined 1static size_tlocal_wcslen (const wchar_t *s){ const wchar_t *ptr; for (ptr = s; *ptr != (wchar_t) 0; ptr++) ; return ptr - s;}# endif# endif#endif/* For those losing systems which don't have 'alloca' we have to add some additional code emulating it. */ #ifdef HAVE_ALLOCA # define freea(p) /* nothing */#else# define alloca(n) malloc (n) # define freea(p) free (p) #endif#ifndef HAVE_LONG_LONG_FORMATstatic intprint_long_long (char *buf, int len, int width, int precision, unsigned long flags, char conversion, unsigned long long number){ int negative = FALSE; char buffer[128]; char *bufferend; char *pointer; int base; static const char *upper = "0123456789ABCDEFX"; static const char *lower = "0123456789abcdefx"; const char *digits; int i; char *p; int count;#define EMIT(c) \ if (p - buf == len - 1) \ { \ *p++ = '\0'; \ return len; \ } \ else \ *p++ = c; p = buf; switch (conversion) { case 'o': base = 8; digits = lower; negative = FALSE; break; case 'x': base = 16; digits = lower; negative = FALSE; break; case 'X': base = 16; digits = upper; negative = FALSE; break; default: base = 10; digits = lower; negative = (long long)number < 0; if (negative) number = -((long long)number); break; } /* Build number */ pointer = bufferend = &buffer[sizeof(buffer) - 1]; *pointer-- = '\0'; for (i = 1; i < (int)sizeof(buffer); i++) { *pointer-- = digits[number % base]; number /= base; if (number == 0) break; } /* Adjust width */ width -= (bufferend - pointer) - 1; /* Adjust precision */ if (precision != -1) { precision -= (bufferend - pointer) - 1; if (precision < 0) precision = 0; flags |= FLAG_ZERO; } /* Adjust width further */ if (negative || (flags & FLAG_SHOWSIGN) || (flags & FLAG_SPACE)) width--; if (flags & FLAG_ALT) { switch (base) { case 16: width -= 2; break; case 8: width--; break; default: break; } } /* Output prefixes spaces if needed */ if (! ((flags & FLAG_LEFT) || ((flags & FLAG_ZERO) && (precision == -1)))) { count = (precision == -1) ? 0 : precision; while (width-- > count) *p++ = ' '; } /* width has been adjusted for signs and alternatives */ if (negative) { EMIT ('-'); } else if (flags & FLAG_SHOWSIGN) { EMIT('+'); } else if (flags & FLAG_SPACE) { EMIT(' '); } if (flags & FLAG_ALT) { switch (base) { case 8: EMIT('0'); break; case 16: EMIT('0'); EMIT(digits[16]); break; default: break; } /* switch base */ } /* Output prefixed zero padding if needed */ if (flags & FLAG_ZERO) { if (precision == -1) precision = width; while (precision-- > 0) { EMIT('0'); width--; } } /* Output the number itself */ while (*(++pointer)) { EMIT(*pointer); } /* Output trailing spaces if needed */ if (flags & FLAG_LEFT) { while (width-- > 0) EMIT(' '); } EMIT('\0'); return p - buf - 1;}#endifchar *vasnprintf (char *resultbuf, size_t *lengthp, const char *format, va_list args){ char_directives d; arguments a; if (printf_parse (format, &d, &a) < 0) { errno = EINVAL; return NULL; }#define CLEANUP() \ free (d.dir); \ if (a.arg) \ free (a.arg); if (printf_fetchargs (args, &a) < 0) { CLEANUP (); errno = EINVAL; return NULL; } { char *buf = (char *) alloca (7 + d.max_width_length + d.max_precision_length + 6); const char *cp; unsigned int i; char_directive *dp; /* Output string accumulator. */ char *result; size_t allocated; size_t length; if (resultbuf != NULL) { result = resultbuf; allocated = *lengthp; } else { result = NULL; allocated = 0; } length = 0; /* Invariants: result is either == resultbuf or == NULL or malloc-allocated. If length > 0, then result != NULL. */#define ENSURE_ALLOCATION(needed) \ if ((needed) > allocated) \ { \ char *memory; \ \ allocated = (allocated > 0 ? 2 * allocated : 12); \ if ((needed) > allocated) \ allocated = (needed); \ if (result == resultbuf || result == NULL) \ memory = (char *) malloc (allocated); \ else \ memory = (char *) realloc (result, allocated); \ \ if (memory == NULL) \ { \ if (!(result == resultbuf || result == NULL)) \ free (result); \ freea (buf); \ CLEANUP (); \ errno = ENOMEM; \ return NULL; \ } \ if (result == resultbuf && length > 0) \ memcpy (memory, result, length); \ result = memory; \ } for (cp = format, i = 0, dp = &d.dir[0]; ; cp = dp->dir_end, i++, dp++) { if (cp != dp->dir_start) { size_t n = dp->dir_start - cp; ENSURE_ALLOCATION (length + n); memcpy (result + length, cp, n); length += n; } if (i == d.count) break; /* Execute a single directive. */ if (dp->conversion == '%') { if (!(dp->arg_index < 0)) abort (); ENSURE_ALLOCATION (length + 1); result[length] = '%'; length += 1; } else { if (!(dp->arg_index >= 0)) abort (); if (dp->conversion == 'n') { switch (a.arg[dp->arg_index].type) { case TYPE_COUNT_SCHAR_POINTER: *a.arg[dp->arg_index].a.a_count_schar_pointer = length; break; case TYPE_COUNT_SHORT_POINTER: *a.arg[dp->arg_index].a.a_count_short_pointer = length; break; case TYPE_COUNT_INT_POINTER: *a.arg[dp->arg_index].a.a_count_int_pointer = length; break; case TYPE_COUNT_LONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longint_pointer = length; break;#ifdef HAVE_LONG_LONG case TYPE_COUNT_LONGLONGINT_POINTER: *a.arg[dp->arg_index].a.a_count_longlongint_pointer = length; break;#endif default: abort (); } } else { arg_type type = a.arg[dp->arg_index].type; char *p; unsigned int prefix_count; int prefixes[2];#if !HAVE_SNPRINTF unsigned int tmp_length; char tmpbuf[700]; char *tmp; /* Allocate a temporary buffer of sufficient size for calling sprintf. */ { unsigned int width; unsigned int precision; width = 0; if (dp->width_start != dp->width_end) { if (dp->width_arg_index >= 0) { int arg; if (!(a.arg[dp->width_arg_index].type == TYPE_INT)) abort (); arg = a.arg[dp->width_arg_index].a.a_int; width = (arg < 0 ? -arg : arg); } else { const char *digitp = dp->width_start; do width = width * 10 + (*digitp++ - '0'); while (digitp != dp->width_end); } } precision = 6; if (dp->precision_start != dp->precision_end) { if (dp->precision_arg_index >= 0) { int arg; if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) abort (); arg = a.arg[dp->precision_arg_index].a.a_int; precision = (arg < 0 ? 0 : arg); } else { const char *digitp = dp->precision_start + 1; precision = 0; while (digitp != dp->precision_end) precision = precision * 10 + (*digitp++ - '0'); } } switch (dp->conversion) { case 'd': case 'i': case 'u':# ifdef HAVE_LONG_LONG if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.30103 /* binary -> decimal */ * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ + 1; /* account for leading sign */ else# endif if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.30103 /* binary -> decimal */ * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ + 1; /* account for leading sign */ else tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.30103 /* binary -> decimal */ * 2 /* estimate for FLAG_GROUP */ ) + 1 /* turn floor into ceil */ + 1; /* account for leading sign */ break; case 'o':# ifdef HAVE_LONG_LONG if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.333334 /* binary -> octal */ ) + 1 /* turn floor into ceil */ + 1; /* account for leading sign */ else# endif if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.333334 /* binary -> octal */ ) + 1 /* turn floor into ceil */ + 1; /* account for leading sign */ else tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.333334 /* binary -> octal */ ) + 1 /* turn floor into ceil */ + 1; /* account for leading sign */ break; case 'x': case 'X':# ifdef HAVE_LONG_LONG if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT) tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) + 1 /* turn floor into ceil */ + 2; /* account for leading sign or alternate form */ else# endif# ifdef HAVE_INT64_AND_I64 if (type == TYPE_INT64 || type == TYPE_UINT64) tmp_length = (unsigned int) (sizeof (unsigned __int64) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) + 1 /* turn floor into ceil */ + 2; /* account for leading sign or alternate form */ else# endif if (type == TYPE_LONGINT || type == TYPE_ULONGINT) tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) + 1 /* turn floor into ceil */ + 2; /* account for leading sign or alternate form */ else tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.25 /* binary -> hexadecimal */ ) + 1 /* turn floor into ceil */ + 2; /* account for leading sign or alternate form */ break; case 'f': case 'F':# ifdef HAVE_LONG_DOUBLE if (type == TYPE_LONGDOUBLE) tmp_length =
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -