When a fatal signal is received, we now write a message to the log that looks like this: F/libc ( 1540): Fatal signal 11 (SIGSEGV) at 0xdeadd00d (code=1) This is useful for debugging fatal signals that turn out not to be fatal. This also changes the signal reset from SIG_IGN to SIG_DFL, so that future non-fatal fatal signals are fatal. The code that blocked SIGUSR1 to avoid being interrupted by the GC has been removed. Also, fix minor issues in format_buffer(). Bug 5035703 Change-Id: I8940af47297b5dcf3cf33537e3483ca5334ed565
		
			
				
	
	
		
			706 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			706 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C) 2010 The Android Open Source Project
 | 
						|
 * All rights reserved.
 | 
						|
 *
 | 
						|
 * Redistribution and use in source and binary forms, with or without
 | 
						|
 * modification, are permitted provided that the following conditions
 | 
						|
 * are met:
 | 
						|
 *  * Redistributions of source code must retain the above copyright
 | 
						|
 *    notice, this list of conditions and the following disclaimer.
 | 
						|
 *  * 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 COPYRIGHT HOLDERS AND CONTRIBUTORS
 | 
						|
 * "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
 | 
						|
 * COPYRIGHT OWNER OR 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.
 | 
						|
 */
 | 
						|
 | 
						|
#include <stdarg.h>
 | 
						|
#include <string.h>
 | 
						|
#include <errno.h>
 | 
						|
#include <unistd.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stddef.h>
 | 
						|
#include "linker_format.h"
 | 
						|
#include "linker_debug.h"
 | 
						|
 | 
						|
/* define UNIT_TESTS to build this file as a single executable that runs
 | 
						|
 * the formatter's unit tests
 | 
						|
 */
 | 
						|
#define xxUNIT_TESTS
 | 
						|
 | 
						|
/*** Generic output sink
 | 
						|
 ***/
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    void *opaque;
 | 
						|
    void (*send)(void *opaque, const char *data, int len);
 | 
						|
} Out;
 | 
						|
 | 
						|
static void
 | 
						|
out_send(Out *o, const void *data, size_t len)
 | 
						|
{
 | 
						|
    o->send(o->opaque, data, (int)len);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
out_send_repeat(Out *o, char ch, int count)
 | 
						|
{
 | 
						|
    char pad[8];
 | 
						|
    const int padSize = (int)sizeof(pad);
 | 
						|
 | 
						|
    memset(pad, ch, sizeof(pad));
 | 
						|
    while (count > 0) {
 | 
						|
        int avail = count;
 | 
						|
        if (avail > padSize) {
 | 
						|
            avail = padSize;
 | 
						|
        }
 | 
						|
        o->send(o->opaque, pad, avail);
 | 
						|
        count -= avail;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* forward declaration */
 | 
						|
static void
 | 
						|
out_vformat(Out *o, const char *format, va_list args);
 | 
						|
 | 
						|
/*** Bounded buffer output
 | 
						|
 ***/
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    Out out[1];
 | 
						|
    char *buffer;
 | 
						|
    char *pos;
 | 
						|
    char *end;
 | 
						|
    int total;
 | 
						|
} BufOut;
 | 
						|
 | 
						|
static void
 | 
						|
buf_out_send(void *opaque, const char *data, int len)
 | 
						|
{
 | 
						|
    BufOut *bo = opaque;
 | 
						|
 | 
						|
    if (len < 0)
 | 
						|
        len = strlen(data);
 | 
						|
 | 
						|
    bo->total += len;
 | 
						|
 | 
						|
    while (len > 0) {
 | 
						|
        int avail = bo->end - bo->pos;
 | 
						|
        if (avail == 0)
 | 
						|
            break;
 | 
						|
        if (avail > len)
 | 
						|
            avail = len;
 | 
						|
        memcpy(bo->pos, data, avail);
 | 
						|
        bo->pos += avail;
 | 
						|
        bo->pos[0] = '\0';
 | 
						|
        len -= avail;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static Out*
 | 
						|
buf_out_init(BufOut *bo, char *buffer, size_t size)
 | 
						|
{
 | 
						|
    if (size == 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    bo->out->opaque = bo;
 | 
						|
    bo->out->send   = buf_out_send;
 | 
						|
    bo->buffer      = buffer;
 | 
						|
    bo->end         = buffer + size - 1;
 | 
						|
    bo->pos         = bo->buffer;
 | 
						|
    bo->pos[0]      = '\0';
 | 
						|
    bo->total       = 0;
 | 
						|
 | 
						|
    return bo->out;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
buf_out_length(BufOut *bo)
 | 
						|
{
 | 
						|
    return bo->total;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
vformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
 | 
						|
{
 | 
						|
    BufOut bo;
 | 
						|
    Out *out;
 | 
						|
 | 
						|
    out = buf_out_init(&bo, buff, buffsize);
 | 
						|
    if (out == NULL)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    out_vformat(out, format, args);
 | 
						|
 | 
						|
    return buf_out_length(&bo);
 | 
						|
}
 | 
						|
 | 
						|
int
 | 
						|
format_buffer(char *buff, size_t buffsize, const char *format, ...)
 | 
						|
{
 | 
						|
    va_list args;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    va_start(args, format);
 | 
						|
    ret = vformat_buffer(buff, buffsize, format, args);
 | 
						|
    va_end(args);
 | 
						|
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* The __stack_chk_fail() function calls __libc_android_log_print()
 | 
						|
 * which calls vsnprintf().
 | 
						|
 *
 | 
						|
 * We define our version of the function here to avoid dragging
 | 
						|
 * about 25 KB of C library routines related to formatting.
 | 
						|
 */
 | 
						|
int
 | 
						|
vsnprintf(char *buff, size_t bufsize, const char *format, va_list args)
 | 
						|
{
 | 
						|
    return format_buffer(buff, bufsize, format, args);
 | 
						|
}
 | 
						|
 | 
						|
#if LINKER_DEBUG
 | 
						|
 | 
						|
#if !LINKER_DEBUG_TO_LOG
 | 
						|
 | 
						|
/*** File descriptor output
 | 
						|
 ***/
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    Out out[1];
 | 
						|
    int fd;
 | 
						|
    int total;
 | 
						|
} FdOut;
 | 
						|
 | 
						|
static void
 | 
						|
fd_out_send(void *opaque, const char *data, int len)
 | 
						|
{
 | 
						|
    FdOut *fdo = opaque;
 | 
						|
 | 
						|
    if (len < 0)
 | 
						|
        len = strlen(data);
 | 
						|
 | 
						|
    while (len > 0) {
 | 
						|
        int ret = write(fdo->fd, data, len);
 | 
						|
        if (ret < 0) {
 | 
						|
            if (errno == EINTR)
 | 
						|
                continue;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        data += ret;
 | 
						|
        len -= ret;
 | 
						|
        fdo->total += ret;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static Out*
 | 
						|
fd_out_init(FdOut *fdo, int  fd)
 | 
						|
{
 | 
						|
    fdo->out->opaque = fdo;
 | 
						|
    fdo->out->send = fd_out_send;
 | 
						|
    fdo->fd = fd;
 | 
						|
    fdo->total = 0;
 | 
						|
 | 
						|
    return fdo->out;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
fd_out_length(FdOut *fdo)
 | 
						|
{
 | 
						|
    return fdo->total;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
int
 | 
						|
format_fd(int fd, const char *format, ...)
 | 
						|
{
 | 
						|
    FdOut fdo;
 | 
						|
    Out* out;
 | 
						|
    va_list args;
 | 
						|
 | 
						|
    out = fd_out_init(&fdo, fd);
 | 
						|
    if (out == NULL)
 | 
						|
        return 0;
 | 
						|
 | 
						|
    va_start(args, format);
 | 
						|
    out_vformat(out, format, args);
 | 
						|
    va_end(args);
 | 
						|
 | 
						|
    return fd_out_length(&fdo);
 | 
						|
}
 | 
						|
 | 
						|
#else /* LINKER_DEBUG_TO_LOG */
 | 
						|
 | 
						|
/*** Log output
 | 
						|
 ***/
 | 
						|
 | 
						|
/* We need our own version of __libc_android_log_vprint, otherwise
 | 
						|
 * the log output is completely broken. Probably due to the fact
 | 
						|
 * that the C library is not initialized yet.
 | 
						|
 *
 | 
						|
 * You can test that by setting CUSTOM_LOG_VPRINT to 0
 | 
						|
 */
 | 
						|
#define  CUSTOM_LOG_VPRINT  1
 | 
						|
 | 
						|
#if CUSTOM_LOG_VPRINT
 | 
						|
 | 
						|
#include <unistd.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <sys/uio.h>
 | 
						|
 | 
						|
static int log_vprint(int prio, const char *tag, const char *fmt, va_list  args)
 | 
						|
{
 | 
						|
    char buf[1024];
 | 
						|
    int result;
 | 
						|
    static int log_fd = -1;
 | 
						|
 | 
						|
    result = vformat_buffer(buf, sizeof buf, fmt, args);
 | 
						|
 | 
						|
    if (log_fd < 0) {
 | 
						|
        log_fd = open("/dev/log/main", O_WRONLY);
 | 
						|
        if (log_fd < 0)
 | 
						|
            return result;
 | 
						|
    }
 | 
						|
 | 
						|
    {
 | 
						|
        ssize_t ret;
 | 
						|
        struct iovec vec[3];
 | 
						|
 | 
						|
        vec[0].iov_base = (unsigned char *) &prio;
 | 
						|
        vec[0].iov_len = 1;
 | 
						|
        vec[1].iov_base = (void *) tag;
 | 
						|
        vec[1].iov_len = strlen(tag) + 1;
 | 
						|
        vec[2].iov_base = (void *) buf;
 | 
						|
        vec[2].iov_len = strlen(buf) + 1;
 | 
						|
 | 
						|
        do {
 | 
						|
            ret = writev(log_fd, vec, 3);
 | 
						|
        } while ((ret < 0) && (errno == EINTR));
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
#define  __libc_android_log_vprint  log_vprint
 | 
						|
 | 
						|
#else /* !CUSTOM_LOG_VPRINT */
 | 
						|
 | 
						|
extern int __libc_android_log_vprint(int  prio, const char* tag, const char*  format, va_list ap);
 | 
						|
 | 
						|
#endif /* !CUSTOM_LOG_VPRINT */
 | 
						|
 | 
						|
int
 | 
						|
format_log(int prio, const char *tag, const char *format, ...)
 | 
						|
{
 | 
						|
    int ret;
 | 
						|
    va_list  args;
 | 
						|
    va_start(args, format);
 | 
						|
    ret = __libc_android_log_vprint(prio, tag, format, args);
 | 
						|
    va_end(args);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* LINKER_DEBUG_TO_LOG */
 | 
						|
 | 
						|
#endif /* LINKER_DEBUG */
 | 
						|
 | 
						|
/*** formatted output implementation
 | 
						|
 ***/
 | 
						|
 | 
						|
/* Parse a decimal string from 'format + *ppos',
 | 
						|
 * return the value, and writes the new position past
 | 
						|
 * the decimal string in '*ppos' on exit.
 | 
						|
 *
 | 
						|
 * NOTE: Does *not* handle a sign prefix.
 | 
						|
 */
 | 
						|
static unsigned
 | 
						|
parse_decimal(const char *format, int *ppos)
 | 
						|
{
 | 
						|
    const char* p = format + *ppos;
 | 
						|
    unsigned result = 0;
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        int ch = *p;
 | 
						|
        unsigned d = (unsigned)(ch - '0');
 | 
						|
 | 
						|
        if (d >= 10U)
 | 
						|
            break;
 | 
						|
 | 
						|
        result = result*10 + d;
 | 
						|
        p++;
 | 
						|
    }
 | 
						|
    *ppos = p - format;
 | 
						|
    return result;
 | 
						|
}
 | 
						|
 | 
						|
/* write an octal/decimal/number into a bounded buffer.
 | 
						|
 * assumes that bufsize > 0, and 'digits' is a string of
 | 
						|
 * digits of at least 'base' values.
 | 
						|
 */
 | 
						|
static void
 | 
						|
format_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
 | 
						|
{
 | 
						|
    char *pos = buffer;
 | 
						|
    char *end = buffer + bufsize - 1;
 | 
						|
 | 
						|
    /* generate digit string in reverse order */
 | 
						|
    while (value) {
 | 
						|
        unsigned d = value % base;
 | 
						|
        value /= base;
 | 
						|
        if (pos < end) {
 | 
						|
            *pos++ = digits[d];
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* special case for 0 */
 | 
						|
    if (pos == buffer) {
 | 
						|
        if (pos < end) {
 | 
						|
            *pos++ = '0';
 | 
						|
        }
 | 
						|
    }
 | 
						|
    pos[0] = '\0';
 | 
						|
 | 
						|
    /* now reverse digit string in-place */
 | 
						|
    end = pos - 1;
 | 
						|
    pos = buffer;
 | 
						|
    while (pos < end) {
 | 
						|
        int ch = pos[0];
 | 
						|
        pos[0] = end[0];
 | 
						|
        end[0] = (char) ch;
 | 
						|
        pos++;
 | 
						|
        end--;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
 | 
						|
static void
 | 
						|
format_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
 | 
						|
{
 | 
						|
    if (isSigned && (int64_t)value < 0) {
 | 
						|
        buffer[0] = '-';
 | 
						|
        buffer += 1;
 | 
						|
        buffsize -= 1;
 | 
						|
        value = (uint64_t)(-(int64_t)value);
 | 
						|
    }
 | 
						|
 | 
						|
    format_number(buffer, buffsize, value, base, "0123456789");
 | 
						|
}
 | 
						|
 | 
						|
/* Write an octal into a buffer, assumes buffsize > 2 */
 | 
						|
static void
 | 
						|
format_octal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
 | 
						|
{
 | 
						|
    format_integer(buffer, buffsize, value, 8, isSigned);
 | 
						|
}
 | 
						|
 | 
						|
/* Write a decimal into a buffer, assumes buffsize > 2 */
 | 
						|
static void
 | 
						|
format_decimal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
 | 
						|
{
 | 
						|
    format_integer(buffer, buffsize, value, 10, isSigned);
 | 
						|
}
 | 
						|
 | 
						|
/* Write an hexadecimal into a buffer, isCap is true for capital alphas.
 | 
						|
 * Assumes bufsize > 2 */
 | 
						|
static void
 | 
						|
format_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
 | 
						|
{
 | 
						|
    const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
 | 
						|
 | 
						|
    format_number(buffer, buffsize, value, 16, digits);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Perform formatted output to an output target 'o' */
 | 
						|
static void
 | 
						|
out_vformat(Out *o, const char *format, va_list args)
 | 
						|
{
 | 
						|
    int nn = 0;
 | 
						|
 | 
						|
    for (;;) {
 | 
						|
        int mm;
 | 
						|
        int padZero = 0;
 | 
						|
        int padLeft = 0;
 | 
						|
        char sign = '\0';
 | 
						|
        int width = -1;
 | 
						|
        int prec  = -1;
 | 
						|
        size_t bytelen = sizeof(int);
 | 
						|
        const char*  str;
 | 
						|
        int slen;
 | 
						|
        char buffer[32];  /* temporary buffer used to format numbers */
 | 
						|
 | 
						|
        char  c;
 | 
						|
 | 
						|
        /* first, find all characters that are not 0 or '%' */
 | 
						|
        /* then send them to the output directly */
 | 
						|
        mm = nn;
 | 
						|
        do {
 | 
						|
            c = format[mm];
 | 
						|
            if (c == '\0' || c == '%')
 | 
						|
                break;
 | 
						|
            mm++;
 | 
						|
        } while (1);
 | 
						|
 | 
						|
        if (mm > nn) {
 | 
						|
            out_send(o, format+nn, mm-nn);
 | 
						|
            nn = mm;
 | 
						|
        }
 | 
						|
 | 
						|
        /* is this it ? then exit */
 | 
						|
        if (c == '\0')
 | 
						|
            break;
 | 
						|
 | 
						|
        /* nope, we are at a '%' modifier */
 | 
						|
        nn++;  // skip it
 | 
						|
 | 
						|
        /* parse flags */
 | 
						|
        for (;;) {
 | 
						|
            c = format[nn++];
 | 
						|
            if (c == '\0') {  /* single trailing '%' ? */
 | 
						|
                c = '%';
 | 
						|
                out_send(o, &c, 1);
 | 
						|
                return;
 | 
						|
            }
 | 
						|
            else if (c == '0') {
 | 
						|
                padZero = 1;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            else if (c == '-') {
 | 
						|
                padLeft = 1;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            else if (c == ' ' || c == '+') {
 | 
						|
                sign = c;
 | 
						|
                continue;
 | 
						|
            }
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        /* parse field width */
 | 
						|
        if ((c >= '0' && c <= '9')) {
 | 
						|
            nn --;
 | 
						|
            width = (int)parse_decimal(format, &nn);
 | 
						|
            c = format[nn++];
 | 
						|
        }
 | 
						|
 | 
						|
        /* parse precision */
 | 
						|
        if (c == '.') {
 | 
						|
            prec = (int)parse_decimal(format, &nn);
 | 
						|
            c = format[nn++];
 | 
						|
        }
 | 
						|
 | 
						|
        /* length modifier */
 | 
						|
        switch (c) {
 | 
						|
        case 'h':
 | 
						|
            bytelen = sizeof(short);
 | 
						|
            if (format[nn] == 'h') {
 | 
						|
                bytelen = sizeof(char);
 | 
						|
                nn += 1;
 | 
						|
            }
 | 
						|
            c = format[nn++];
 | 
						|
            break;
 | 
						|
        case 'l':
 | 
						|
            bytelen = sizeof(long);
 | 
						|
            if (format[nn] == 'l') {
 | 
						|
                bytelen = sizeof(long long);
 | 
						|
                nn += 1;
 | 
						|
            }
 | 
						|
            c = format[nn++];
 | 
						|
            break;
 | 
						|
        case 'z':
 | 
						|
            bytelen = sizeof(size_t);
 | 
						|
            c = format[nn++];
 | 
						|
            break;
 | 
						|
        case 't':
 | 
						|
            bytelen = sizeof(ptrdiff_t);
 | 
						|
            c = format[nn++];
 | 
						|
            break;
 | 
						|
        default:
 | 
						|
            ;
 | 
						|
        }
 | 
						|
 | 
						|
        /* conversion specifier */
 | 
						|
        if (c == 's') {
 | 
						|
            /* string */
 | 
						|
            str = va_arg(args, const char*);
 | 
						|
        } else if (c == 'c') {
 | 
						|
            /* character */
 | 
						|
            /* NOTE: char is promoted to int when passed through the stack */
 | 
						|
            buffer[0] = (char) va_arg(args, int);
 | 
						|
            buffer[1] = '\0';
 | 
						|
            str = buffer;
 | 
						|
        } else if (c == 'p') {
 | 
						|
            uint64_t  value = (uintptr_t) va_arg(args, void*);
 | 
						|
            buffer[0] = '0';
 | 
						|
            buffer[1] = 'x';
 | 
						|
            format_hex(buffer + 2, sizeof buffer-2, value, 0);
 | 
						|
            str = buffer;
 | 
						|
        } else {
 | 
						|
            /* integers - first read value from stack */
 | 
						|
            uint64_t value;
 | 
						|
            int isSigned = (c == 'd' || c == 'i' || c == 'o');
 | 
						|
 | 
						|
            /* NOTE: int8_t and int16_t are promoted to int when passed
 | 
						|
             *       through the stack
 | 
						|
             */
 | 
						|
            switch (bytelen) {
 | 
						|
            case 1: value = (uint8_t)  va_arg(args, int); break;
 | 
						|
            case 2: value = (uint16_t) va_arg(args, int); break;
 | 
						|
            case 4: value = va_arg(args, uint32_t); break;
 | 
						|
            case 8: value = va_arg(args, uint64_t); break;
 | 
						|
            default: return;  /* should not happen */
 | 
						|
            }
 | 
						|
 | 
						|
            /* sign extension, if needed */
 | 
						|
            if (isSigned) {
 | 
						|
                int shift = 64 - 8*bytelen;
 | 
						|
                value = (uint64_t)(((int64_t)(value << shift)) >> shift);
 | 
						|
            }
 | 
						|
 | 
						|
            /* format the number properly into our buffer */
 | 
						|
            switch (c) {
 | 
						|
            case 'i': case 'd':
 | 
						|
                format_integer(buffer, sizeof buffer, value, 10, isSigned);
 | 
						|
                break;
 | 
						|
            case 'o':
 | 
						|
                format_integer(buffer, sizeof buffer, value, 8, isSigned);
 | 
						|
                break;
 | 
						|
            case 'x': case 'X':
 | 
						|
                format_hex(buffer, sizeof buffer, value, (c == 'X'));
 | 
						|
                break;
 | 
						|
            default:
 | 
						|
                buffer[0] = '\0';
 | 
						|
            }
 | 
						|
            /* then point to it */
 | 
						|
            str = buffer;
 | 
						|
        }
 | 
						|
 | 
						|
        /* if we are here, 'str' points to the content that must be
 | 
						|
         * outputted. handle padding and alignment now */
 | 
						|
 | 
						|
        slen = strlen(str);
 | 
						|
 | 
						|
        if (slen < width && !padLeft) {
 | 
						|
            char padChar = padZero ? '0' : ' ';
 | 
						|
            out_send_repeat(o, padChar, width - slen);
 | 
						|
        }
 | 
						|
 | 
						|
        out_send(o, str, slen);
 | 
						|
 | 
						|
        if (slen < width && padLeft) {
 | 
						|
            char padChar = padZero ? '0' : ' ';
 | 
						|
            out_send_repeat(o, padChar, width - slen);
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
#ifdef UNIT_TESTS
 | 
						|
 | 
						|
#include <stdio.h>
 | 
						|
 | 
						|
static int   gFails = 0;
 | 
						|
 | 
						|
#define  MARGIN  40
 | 
						|
 | 
						|
#define  UTEST_CHECK(condition,message) \
 | 
						|
    printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
 | 
						|
    if (!(condition)) { \
 | 
						|
        printf("KO\n"); \
 | 
						|
        gFails += 1; \
 | 
						|
    } else { \
 | 
						|
        printf("ok\n"); \
 | 
						|
    }
 | 
						|
 | 
						|
static void
 | 
						|
utest_BufOut(void)
 | 
						|
{
 | 
						|
    char buffer[16];
 | 
						|
    BufOut bo[1];
 | 
						|
    Out* out;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    buffer[0] = '1';
 | 
						|
    out = buf_out_init(bo, buffer, sizeof buffer);
 | 
						|
    UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
 | 
						|
    out_send(out, "abc", 3);
 | 
						|
    UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
 | 
						|
    out_send_repeat(out, 'X', 4);
 | 
						|
    UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
 | 
						|
    buffer[sizeof buffer-1] = 'x';
 | 
						|
    out_send_repeat(out, 'Y', 2*sizeof(buffer));
 | 
						|
    UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
 | 
						|
 | 
						|
    out = buf_out_init(bo, buffer, sizeof buffer);
 | 
						|
    out_send_repeat(out, 'X', 2*sizeof(buffer));
 | 
						|
    ret = buf_out_length(bo);
 | 
						|
    UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
utest_expect(const char*  result, const char*  format, ...)
 | 
						|
{
 | 
						|
    va_list args;
 | 
						|
    BufOut bo[1];
 | 
						|
    char buffer[256];
 | 
						|
    Out* out = buf_out_init(bo, buffer, sizeof buffer);
 | 
						|
 | 
						|
    printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
 | 
						|
    va_start(args, format);
 | 
						|
    out_vformat(out, format, args);
 | 
						|
    va_end(args);
 | 
						|
 | 
						|
    if (strcmp(result, buffer)) {
 | 
						|
        printf("KO. got '%s' expecting '%s'\n", buffer, result);
 | 
						|
        gFails += 1;
 | 
						|
    } else {
 | 
						|
        printf("ok. got '%s'\n", result);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
int  main(void)
 | 
						|
{
 | 
						|
    utest_BufOut();
 | 
						|
    utest_expect("", "");
 | 
						|
    utest_expect("a", "a");
 | 
						|
    utest_expect("01234", "01234", "");
 | 
						|
    utest_expect("01234", "%s", "01234");
 | 
						|
    utest_expect("aabbcc", "aa%scc", "bb");
 | 
						|
    utest_expect("a", "%c", 'a');
 | 
						|
    utest_expect("1234", "%d", 1234);
 | 
						|
    utest_expect("-8123", "%d", -8123);
 | 
						|
    utest_expect("16", "%hd", 0x7fff0010);
 | 
						|
    utest_expect("16", "%hhd", 0x7fffff10);
 | 
						|
    utest_expect("68719476736", "%lld", 0x1000000000LL);
 | 
						|
    utest_expect("70000", "%ld", 70000);
 | 
						|
    utest_expect("0xb0001234", "%p", (void*)0xb0001234);
 | 
						|
    utest_expect("12ab", "%x", 0x12ab);
 | 
						|
    utest_expect("12AB", "%X", 0x12ab);
 | 
						|
    utest_expect("00123456", "%08x", 0x123456);
 | 
						|
    utest_expect("01234", "0%d", 1234);
 | 
						|
    utest_expect(" 1234", "%5d", 1234);
 | 
						|
    utest_expect("01234", "%05d", 1234);
 | 
						|
    utest_expect("    1234", "%8d", 1234);
 | 
						|
    utest_expect("1234    ", "%-8d", 1234);
 | 
						|
    utest_expect("abcdef     ", "%-11s", "abcdef");
 | 
						|
    utest_expect("something:1234", "%s:%d", "something", 1234);
 | 
						|
    utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
 | 
						|
    utest_expect("5,0x0", "%d,%p", 5, NULL);
 | 
						|
    utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
 | 
						|
    return gFails != 0;
 | 
						|
}
 | 
						|
 | 
						|
#endif /* UNIT_TESTS */
 |