summaryrefslogtreecommitdiff
path: root/xprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'xprintf.c')
-rw-r--r--xprintf.c591
1 files changed, 0 insertions, 591 deletions
diff --git a/xprintf.c b/xprintf.c
deleted file mode 100644
index 7efb786..0000000
--- a/xprintf.c
+++ /dev/null
@@ -1,591 +0,0 @@
-/*
- * Copyright (c) 2010 Richard Braun.
- * 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.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 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 AUTHOR 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.
- */
-
-#include <stdio.h>
-#include <limits.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <unistd.h>
-#include <pthread.h>
-
-#include "xprintf.h"
-
-/*
- * Formatting flags.
- *
- * FORMAT_LOWER must be 0x20 as it is OR'd with digits, eg.
- * '0': 0x30 | 0x20 => 0x30 ('0')
- * 'A': 0x41 | 0x20 => 0x61 ('a')
- */
-#define FORMAT_ALT_FORM 0x01 /* "Alternate form" */
-#define FORMAT_ZERO_PAD 0x02 /* Zero padding on the left */
-#define FORMAT_LEFT_JUSTIFY 0x04 /* Align text on the left */
-#define FORMAT_BLANK 0x08 /* Blank space before positive number */
-#define FORMAT_SIGN 0x10 /* Always place a sign (either + or -) */
-#define FORMAT_LOWER 0x20 /* To lowercase (for %x) */
-#define FORMAT_CONV_SIGNED 0x40 /* Format specifies signed conversion */
-
-enum {
- MODIFIER_NONE,
- MODIFIER_CHAR,
- MODIFIER_SHORT,
- MODIFIER_LONG,
- MODIFIER_LONGLONG,
- MODIFIER_PTR, /* Used only for %p */
- MODIFIER_SIZE,
- MODIFIER_PTRDIFF
-};
-
-enum {
- SPECIFIER_INVALID,
- SPECIFIER_INT,
- SPECIFIER_CHAR,
- SPECIFIER_STR,
- SPECIFIER_NRCHARS,
- SPECIFIER_PERCENT
-};
-
-/*
- * Size for the temporary number buffer. The minimum base is 8 so 3 bits
- * are consumed per digit. Add one to round up. The conversion algorithm
- * doesn't use the null byte.
- */
-#define MAX_NUM_SIZE (((sizeof(unsigned long long) * CHAR_BIT) / 3) + 1)
-
-/*
- * Special size for xvsnprintf(), used by xsprintf()/xvsprintf() when the
- * buffer size is unknown.
- */
-#define XPRINT_NOLIMIT ((size_t)-1)
-
-/*
- * Size of the static buffer used by xprintf()/xvprintf().
- */
-#define XPRINT_BUFSIZE 1024
-
-static char xprint_buffer[XPRINT_BUFSIZE];
-static pthread_mutex_t xprint_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-static const char digits[] = "0123456789ABCDEF";
-
-static inline char *
-xputchar(char *str, char *end, char c)
-{
- if (str < end)
- *str = c;
-
- str++;
-
- return str;
-}
-
-static inline int
-xisdigit(char c)
-{
- return (c >= '0') && (c <= '9');
-}
-
-int
-xprintf(const char *format, ...)
-{
- va_list ap;
- int length;
-
- va_start(ap, format);
- length = xvprintf(format, ap);
- va_end(ap);
-
- return length;
-}
-
-int
-xvprintf(const char *format, va_list ap)
-{
- size_t size;
- int length;
-
- pthread_mutex_lock(&xprint_mutex);
- length = xvsnprintf(xprint_buffer, sizeof(xprint_buffer), format, ap);
- size = ((unsigned int)length >= sizeof(xprint_buffer))
- ? sizeof(xprint_buffer) - 1
- : (unsigned int)length;
- fwrite(xprint_buffer, 1, size, stdout);
- pthread_mutex_unlock(&xprint_mutex);
-
- return length;
-}
-
-int
-xsprintf(char *str, const char *format, ...)
-{
- va_list ap;
- int length;
-
- va_start(ap, format);
- length = xvsprintf(str, format, ap);
- va_end(ap);
-
- return length;
-}
-
-int
-xvsprintf(char *str, const char *format, va_list ap)
-{
- return xvsnprintf(str, XPRINT_NOLIMIT, format, ap);
-}
-
-int
-xsnprintf(char *str, size_t size, const char *format, ...)
-{
- va_list ap;
- int length;
-
- va_start(ap, format);
- length = xvsnprintf(str, size, format, ap);
- va_end(ap);
-
- return length;
-}
-
-int
-xvsnprintf(char *str, size_t size, const char *format, va_list ap)
-{
- unsigned long long n;
- int i, len, found, flags, width, precision, modifier, specifier, shift;
- unsigned char r, base, mask;
- char c, *s, *start, *end, sign, tmp[MAX_NUM_SIZE];
-
- start = str;
-
- if (size == 0)
- end = NULL;
- else if (size == XPRINT_NOLIMIT)
- end = (char *)-1;
- else
- end = start + size - 1;
-
- while ((c = *format) != '\0') {
- if (c != '%') {
- str = xputchar(str, end, c);
- format++;
- continue;
- }
-
- /* Flags */
-
- found = 1;
- flags = 0;
-
- do {
- format++;
- c = *format;
-
- switch (c) {
- case '#':
- flags |= FORMAT_ALT_FORM;
- break;
- case '0':
- flags |= FORMAT_ZERO_PAD;
- break;
- case '-':
- flags |= FORMAT_LEFT_JUSTIFY;
- break;
- case ' ':
- flags |= FORMAT_BLANK;
- break;
- case '+':
- flags |= FORMAT_SIGN;
- break;
- default:
- found = 0;
- break;
- }
- } while (found);
-
- /* Width */
-
- if (xisdigit(c)) {
- width = 0;
-
- while (xisdigit(c)) {
- width = width * 10 + (c - '0');
- format++;
- c = *format;
- }
- } else if (c == '*') {
- width = va_arg(ap, int);
-
- if (width < 0) {
- flags |= FORMAT_LEFT_JUSTIFY;
- width = -width;
- }
-
- format++;
- c = *format;
- } else {
- width = 0;
- }
-
- /* Precision */
-
- if (c == '.') {
- format++;
- c = *format;
-
- if (xisdigit(c)) {
- precision = 0;
-
- while (xisdigit(c)) {
- precision = precision * 10 + (c - '0');
- format++;
- c = *format;
- }
- } else if (c == '*') {
- precision = va_arg(ap, int);
-
- if (precision < 0)
- precision = 0;
-
- format++;
- c = *format;
- } else {
- precision = 0;
- }
- } else {
- /* precision is >= 0 only if explicit */
- precision = -1;
- }
-
- /* Length modifier */
-
- switch (c) {
- case 'h':
- case 'l':
- format++;
-
- if (c == *format) {
- modifier = (c == 'h') ? MODIFIER_CHAR : MODIFIER_LONGLONG;
- goto skip_modifier;
- } else {
- modifier = (c == 'h') ? MODIFIER_SHORT : MODIFIER_LONG;
- c = *format;
- }
-
- break;
- case 'z':
- modifier = MODIFIER_SIZE;
- goto skip_modifier;
- case 't':
- modifier = MODIFIER_PTRDIFF;
-skip_modifier:
- format++;
- c = *format;
- break;
- default:
- modifier = MODIFIER_NONE;
- break;
- }
-
- /* Specifier */
-
- switch (c) {
- case 'd':
- case 'i':
- flags |= FORMAT_CONV_SIGNED;
- case 'u':
- base = 10;
- goto integer;
- case 'o':
- base = 8;
- goto integer;
- case 'p':
- flags |= FORMAT_ALT_FORM;
- modifier = MODIFIER_PTR;
- case 'x':
- flags |= FORMAT_LOWER;
- case 'X':
- base = 16;
-integer:
- specifier = SPECIFIER_INT;
- break;
- case 'c':
- specifier = SPECIFIER_CHAR;
- break;
- case 's':
- specifier = SPECIFIER_STR;
- break;
- case 'n':
- specifier = SPECIFIER_NRCHARS;
- break;
- case '%':
- specifier = SPECIFIER_PERCENT;
- break;
- default:
- specifier = SPECIFIER_INVALID;
- break;
- }
-
- /* Output */
-
- switch (specifier) {
- case SPECIFIER_INT:
- switch (modifier) {
- case MODIFIER_CHAR:
- if (flags & FORMAT_CONV_SIGNED)
- n = (signed char)va_arg(ap, int);
- else
- n = (unsigned char)va_arg(ap, int);
- break;
- case MODIFIER_SHORT:
- if (flags & FORMAT_CONV_SIGNED)
- n = (short)va_arg(ap, int);
- else
- n = (unsigned short)va_arg(ap, int);
- break;
- case MODIFIER_LONG:
- if (flags & FORMAT_CONV_SIGNED)
- n = va_arg(ap, long);
- else
- n = va_arg(ap, unsigned long);
- break;
- case MODIFIER_LONGLONG:
- if (flags & FORMAT_CONV_SIGNED)
- n = va_arg(ap, long long);
- else
- n = va_arg(ap, unsigned long long);
- break;
- case MODIFIER_PTR:
- n = (unsigned long)va_arg(ap, void *);
- break;
- case MODIFIER_SIZE:
- if (flags & FORMAT_CONV_SIGNED)
- n = va_arg(ap, ssize_t);
- else
- n = va_arg(ap, size_t);
- break;
- case MODIFIER_PTRDIFF:
- n = va_arg(ap, ptrdiff_t);
- break;
- default:
- if (flags & FORMAT_CONV_SIGNED)
- n = va_arg(ap, int);
- else
- n = va_arg(ap, unsigned int);
- break;
- }
-
- if ((flags & FORMAT_LEFT_JUSTIFY) || (precision >= 0))
- flags &= ~FORMAT_ZERO_PAD;
-
- sign = 0;
-
- if (flags & FORMAT_ALT_FORM) {
- /* '0' for octal */
- width--;
-
- /* '0x' or '0X' for hexadecimal */
- if (base == 16)
- width--;
- } else if (flags & FORMAT_CONV_SIGNED) {
- if ((long long)n < 0) {
- sign = '-';
- width--;
- n = -(long long)n;
- } else if (flags & FORMAT_SIGN) {
- /* FORMAT_SIGN must precede FORMAT_BLANK. */
- sign = '+';
- width--;
- } else if (flags & FORMAT_BLANK) {
- sign = ' ';
- width--;
- }
- }
-
- /* Conversion, in reverse order */
-
- i = 0;
-
- if (n == 0) {
- if (precision != 0)
- tmp[i++] = '0';
- } else if (base == 10) {
- /*
- * Try to avoid 64 bits operations if the processor doesn't
- * support them. Note that even when using modulus and
- * division operators close to each other, the compiler may
- * forge two calls to __udivdi3() and __umoddi3() instead of
- * one to __udivmoddi3(), whereas processor instructions are
- * generally correctly used once, giving both the remainder
- * and the quotient, through plain or reciprocal division.
- */
-#ifndef __LP64__
- if (modifier == MODIFIER_LONGLONG) {
-#endif /* __LP64__ */
- do {
- r = n % 10;
- n /= 10;
- tmp[i++] = digits[r];
- } while (n != 0);
-#ifndef __LP64__
- } else {
- unsigned long m;
-
- m = (unsigned long)n;
-
- do {
- r = m % 10;
- m /= 10;
- tmp[i++] = digits[r];
- } while (m != 0);
- }
-#endif /* __LP64__ */
- } else {
- mask = base - 1;
- shift = (base == 8) ? 3 : 4;
-
- do {
- r = (unsigned char)n & mask;
- n >>= shift;
- tmp[i++] = digits[r] | (flags & FORMAT_LOWER);
- } while (n != 0);
- }
-
- if (i > precision)
- precision = i;
-
- width -= precision;
-
- if (!(flags & (FORMAT_LEFT_JUSTIFY | FORMAT_ZERO_PAD)))
- while (width-- > 0)
- str = xputchar(str, end, ' ');
-
- if (flags & FORMAT_ALT_FORM) {
- str = xputchar(str, end, '0');
-
- if (base == 16)
- str = xputchar(str, end, 'X' | (flags & FORMAT_LOWER));
- } else if (sign) {
- str = xputchar(str, end, sign);
- }
-
- if (!(flags & FORMAT_LEFT_JUSTIFY)) {
- c = (flags & FORMAT_ZERO_PAD) ? '0' : ' ';
-
- while (width-- > 0)
- str = xputchar(str, end, c);
- }
-
- while (i < precision--)
- str = xputchar(str, end, '0');
-
- while (i-- > 0)
- str = xputchar(str, end, tmp[i]);
-
- while (width-- > 0)
- str = xputchar(str, end, ' ');
-
- break;
- case SPECIFIER_CHAR:
- c = (unsigned char)va_arg(ap, int);
-
- if (!(flags & FORMAT_LEFT_JUSTIFY))
- while (--width > 0)
- str = xputchar(str, end, ' ');
-
- str = xputchar(str, end, c);
-
- while (--width > 0)
- str = xputchar(str, end, ' ');
-
- break;
- case SPECIFIER_STR:
- s = va_arg(ap, char *);
-
- if (s == NULL)
- s = "(null)";
-
- len = 0;
-
- for (len = 0; s[len] != '\0'; len++)
- if (len == precision)
- break;
-
- if (!(flags & FORMAT_LEFT_JUSTIFY))
- while (len < width--)
- str = xputchar(str, end, ' ');
-
- for (i = 0; i < len; i++) {
- str = xputchar(str, end, *s);
- s++;
- }
-
- while (len < width--)
- str = xputchar(str, end, ' ');
-
- break;
- case SPECIFIER_NRCHARS:
- if (modifier == MODIFIER_CHAR) {
- signed char *ptr = va_arg(ap, signed char *);
- *ptr = str - start;
- } else if (modifier == MODIFIER_SHORT) {
- short *ptr = va_arg(ap, short *);
- *ptr = str - start;
- } else if (modifier == MODIFIER_LONG) {
- long *ptr = va_arg(ap, long *);
- *ptr = str - start;
- } else if (modifier == MODIFIER_LONGLONG) {
- long long *ptr = va_arg(ap, long long *);
- *ptr = str - start;
- } else if (modifier == MODIFIER_SIZE) {
- ssize_t *ptr = va_arg(ap, ssize_t *);
- *ptr = str - start;
- } else if (modifier == MODIFIER_PTRDIFF) {
- ptrdiff_t *ptr = va_arg(ap, ptrdiff_t *);
- *ptr = str - start;
- } else {
- int *ptr = va_arg(ap, int *);
- *ptr = str - start;
- }
-
- break;
- case SPECIFIER_PERCENT:
- case SPECIFIER_INVALID:
- str = xputchar(str, end, '%');
- break;
- default:
- break;
- }
-
- if (specifier != SPECIFIER_INVALID)
- format++;
- }
-
- if (str < end)
- *str = '\0';
- else if (end != NULL)
- *end = '\0';
-
- return str - start;
-}