15c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*
25c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * Copyright (C) 2010 The Android Open Source Project
35c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * All rights reserved.
45c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
55c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * Redistribution and use in source and binary forms, with or without
65c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * modification, are permitted provided that the following conditions
75c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * are met:
85c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *  * Redistributions of source code must retain the above copyright
95c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *    notice, this list of conditions and the following disclaimer.
105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *  * Redistributions in binary form must reproduce the above copyright
115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *    notice, this list of conditions and the following disclaimer in
125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *    the documentation and/or other materials provided with the
135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *    distribution.
145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * SUCH DAMAGE.
275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <stdarg.h>
305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <string.h>
315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <errno.h>
325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <unistd.h>
335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <stdint.h>
345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <stddef.h>
355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include "linker_format.h"
365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include "linker_debug.h"
375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* define UNIT_TESTS to build this file as a single executable that runs
395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * the formatter's unit tests
405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define xxUNIT_TESTS
425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** Generic output sink
445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnertypedef struct {
475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    void *opaque;
485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    void (*send)(void *opaque, const char *data, int len);
495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner} Out;
505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerout_send(Out *o, const void *data, size_t len)
535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    o->send(o->opaque, data, (int)len);
555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerout_send_repeat(Out *o, char ch, int count)
595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char pad[8];
615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    const int padSize = (int)sizeof(pad);
625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    memset(pad, ch, sizeof(pad));
645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (count > 0) {
655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int avail = count;
665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (avail > padSize) {
675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            avail = padSize;
685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        o->send(o->opaque, pad, avail);
705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        count -= avail;
715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* forward declaration */
755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerout_vformat(Out *o, const char *format, va_list args);
775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** Bounded buffer output
795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnertypedef struct {
825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out out[1];
835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *buffer;
845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *pos;
855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *end;
865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int total;
875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner} BufOut;
885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerbuf_out_send(void *opaque, const char *data, int len)
915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    BufOut *bo = opaque;
935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (len < 0)
955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        len = strlen(data);
965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->total += len;
985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (len > 0) {
1005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int avail = bo->end - bo->pos;
1015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (avail == 0)
1025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
1035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (avail > len)
1045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            avail = len;
1055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        memcpy(bo->pos, data, avail);
1065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        bo->pos += avail;
1075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        bo->pos[0] = '\0';
1085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        len -= avail;
1095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
1105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
1115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic Out*
1135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerbuf_out_init(BufOut *bo, char *buffer, size_t size)
1145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
1155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (size == 0)
1165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        return NULL;
1175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->out->opaque = bo;
1195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->out->send   = buf_out_send;
1205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->buffer      = buffer;
1215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->end         = buffer + size - 1;
1225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->pos         = bo->buffer;
1235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->pos[0]      = '\0';
1245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    bo->total       = 0;
1255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return bo->out;
1275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
1285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int
1305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerbuf_out_length(BufOut *bo)
1315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
1325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return bo->total;
1335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
1345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int
1365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnervformat_buffer(char *buff, size_t buffsize, const char *format, va_list args)
1375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
1385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    BufOut bo;
1395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out *out;
1405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = buf_out_init(&bo, buff, buffsize);
1425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (out == NULL)
1435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        return 0;
1445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_vformat(out, format, args);
1465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return buf_out_length(&bo);
1485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
1495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint
1515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_buffer(char *buff, size_t buffsize, const char *format, ...)
1525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
1535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list args;
1545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int ret;
1555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
1575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    ret = vformat_buffer(buff, buffsize, format, args);
1585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
1595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return ret;
1615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
1625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* The __stack_chk_fail() function calls __libc_android_log_print()
1645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * which calls vsnprintf().
1655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
1665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * We define our version of the function here to avoid dragging
1675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * about 25 KB of C library routines related to formatting.
1685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
1695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint
1705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnervsnprintf(char *buff, size_t bufsize, const char *format, va_list args)
1715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
1725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return format_buffer(buff, bufsize, format, args);
1735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
1745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#if LINKER_DEBUG
1765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#if !LINKER_DEBUG_TO_LOG
1785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** File descriptor output
1805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
1815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnertypedef struct {
1835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out out[1];
1845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int fd;
1855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int total;
1865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner} FdOut;
1875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
1895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerfd_out_send(void *opaque, const char *data, int len)
1905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
1915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    FdOut *fdo = opaque;
1925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (len < 0)
1945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        len = strlen(data);
1955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (len > 0) {
1975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int ret = write(fdo->fd, data, len);
1985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (ret < 0) {
1995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (errno == EINTR)
2005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
2015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
2025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
2035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        data += ret;
2045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        len -= ret;
2055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        fdo->total += ret;
2065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
2075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic Out*
2105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerfd_out_init(FdOut *fdo, int  fd)
2115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->out->opaque = fdo;
2135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->out->send = fd_out_send;
2145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->fd = fd;
2155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->total = 0;
2165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return fdo->out;
2185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int
2215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerfd_out_length(FdOut *fdo)
2225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return fdo->total;
2245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint
2285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_fd(int fd, const char *format, ...)
2295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    FdOut fdo;
2315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out* out;
2325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list args;
2335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = fd_out_init(&fdo, fd);
2355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (out == NULL)
2365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        return 0;
2375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
2395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_vformat(out, format, args);
2405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
2415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return fd_out_length(&fdo);
2435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#else /* LINKER_DEBUG_TO_LOG */
2465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** Log output
2485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
2495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* We need our own version of __libc_android_log_vprint, otherwise
2515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * the log output is completely broken. Probably due to the fact
2525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * that the C library is not initialized yet.
2535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
2545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * You can test that by setting CUSTOM_LOG_VPRINT to 0
2555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
2565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  CUSTOM_LOG_VPRINT  1
2575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#if CUSTOM_LOG_VPRINT
2595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <unistd.h>
2615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <fcntl.h>
2625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <sys/uio.h>
2635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int log_vprint(int prio, const char *tag, const char *fmt, va_list  args)
2655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char buf[1024];
2675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int result;
2685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    static int log_fd = -1;
2695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    result = vformat_buffer(buf, sizeof buf, fmt, args);
2715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (log_fd < 0) {
2735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        log_fd = open("/dev/log/main", O_WRONLY);
2745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (log_fd < 0)
2755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            return result;
2765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
2775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    {
2795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        ssize_t ret;
2805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        struct iovec vec[3];
2815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[0].iov_base = (unsigned char *) &prio;
2835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[0].iov_len = 1;
2845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[1].iov_base = (void *) tag;
2855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[1].iov_len = strlen(tag) + 1;
2865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[2].iov_base = (void *) buf;
2875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[2].iov_len = strlen(buf) + 1;
2885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        do {
2905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            ret = writev(log_fd, vec, 3);
2915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } while ((ret < 0) && (errno == EINTR));
2925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
2935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return result;
2945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  __libc_android_log_vprint  log_vprint
2975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#else /* !CUSTOM_LOG_VPRINT */
2995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerextern int __libc_android_log_vprint(int  prio, const char* tag, const char*  format, va_list ap);
3015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* !CUSTOM_LOG_VPRINT */
3035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint
3055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_log(int prio, const char *tag, const char *format, ...)
3065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int ret;
3085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list  args;
3095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
3105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    ret = __libc_android_log_vprint(prio, tag, format, args);
3115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
3125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return ret;
3135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
3145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* LINKER_DEBUG_TO_LOG */
3165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* LINKER_DEBUG */
3185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** formatted output implementation
3205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
3215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Parse a decimal string from 'format + *ppos',
3235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * return the value, and writes the new position past
3245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * the decimal string in '*ppos' on exit.
3255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
3265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * NOTE: Does *not* handle a sign prefix.
3275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
3285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic unsigned
3295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerparse_decimal(const char *format, int *ppos)
3305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    const char* p = format + *ppos;
3325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    unsigned result = 0;
3335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    for (;;) {
3355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int ch = *p;
3365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        unsigned d = (unsigned)(ch - '0');
3375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (d >= 10U)
3395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
3405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        result = result*10 + d;
3425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        p++;
3435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    *ppos = p - format;
3455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return result;
3465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
3475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* write an octal/decimal/number into a bounded buffer.
3495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * assumes that bufsize > 0, and 'digits' is a string of
3505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * digits of at least 'base' values.
3515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
3525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
3535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
3545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *pos = buffer;
3565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *end = buffer + bufsize - 1;
3575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    /* generate digit string in reverse order */
3595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (value) {
3605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        unsigned d = value % base;
3615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        value /= base;
3625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (pos < end) {
3635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            *pos++ = digits[d];
3645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
3655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    /* special case for 0 */
3685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (pos == buffer) {
3695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (pos < end) {
3705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            *pos++ = '0';
3715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
3725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    pos[0] = '\0';
3745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    /* now reverse digit string in-place */
3765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    end = pos - 1;
3775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    pos = buffer;
3785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (pos < end) {
3795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int ch = pos[0];
3805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        pos[0] = end[0];
3815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        end[0] = (char) ch;
3825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        pos++;
3835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        end--;
3845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
3865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
3885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
3895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
3905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (isSigned && (int64_t)value < 0) {
3925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        buffer[0] = '-';
3935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        buffer += 1;
3945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        buffsize -= 1;
3955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        value = (uint64_t)(-(int64_t)value);
3965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    format_number(buffer, buffsize, value, base, "0123456789");
3995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Write an octal into a buffer, assumes buffsize > 2 */
4025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_octal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
4045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
4055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    format_integer(buffer, buffsize, value, 8, isSigned);
4065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Write a decimal into a buffer, assumes buffsize > 2 */
4095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_decimal(char *buffer, size_t buffsize, uint64_t value, int isSigned)
4115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
4125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    format_integer(buffer, buffsize, value, 10, isSigned);
4135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Write an hexadecimal into a buffer, isCap is true for capital alphas.
4165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * Assumes bufsize > 2 */
4175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
4195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
4205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
4215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    format_number(buffer, buffsize, value, 16, digits);
4235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Perform formatted output to an output target 'o' */
4275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerout_vformat(Out *o, const char *format, va_list args)
4295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
430ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    int nn = 0;
4315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    for (;;) {
433ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int mm;
434ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int padZero = 0;
435ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int padLeft = 0;
436ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        char sign = '\0';
437ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int width = -1;
438ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int prec  = -1;
439ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        size_t bytelen = sizeof(int);
440ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        const char*  str;
441ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int slen;
442ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        char buffer[32];  /* temporary buffer used to format numbers */
443ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden
4445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        char  c;
4455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* first, find all characters that are not 0 or '%' */
4475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* then send them to the output directly */
4485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        mm = nn;
4495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        do {
4505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[mm];
4515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (c == '\0' || c == '%')
4525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
4535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            mm++;
4545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } while (1);
4555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (mm > nn) {
4575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            out_send(o, format+nn, mm-nn);
4585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            nn = mm;
4595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
4605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* is this it ? then exit */
4625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (c == '\0')
4635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
4645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* nope, we are at a '%' modifier */
4665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        nn++;  // skip it
4675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* parse flags */
4695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        for (;;) {
4705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
4715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (c == '\0') {  /* single trailing '%' ? */
4725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                c = '%';
4735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                out_send(o, &c, 1);
4745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                return;
4755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            else if (c == '0') {
4775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                padZero = 1;
4785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
4795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            else if (c == '-') {
4815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                padLeft = 1;
4825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
4835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            else if (c == ' ' || c == '+') {
4855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                sign = c;
4865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
4875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
4895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
4905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* parse field width */
4925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if ((c >= '0' && c <= '9')) {
4935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            nn --;
4945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            width = (int)parse_decimal(format, &nn);
4955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
4965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
4975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* parse precision */
4995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (c == '.') {
5005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            prec = (int)parse_decimal(format, &nn);
5015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* length modifier */
5055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        switch (c) {
5065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 'h':
5075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(short);
5085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (format[nn] == 'h') {
5095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                bytelen = sizeof(char);
5105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                nn += 1;
5115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 'l':
5155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(long);
5165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (format[nn] == 'l') {
5175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                bytelen = sizeof(long long);
5185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                nn += 1;
5195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 'z':
5235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(size_t);
5245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 't':
5275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(ptrdiff_t);
5285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        default:
5315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            ;
5325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* conversion specifier */
5355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (c == 's') {
5365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* string */
5375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = va_arg(args, const char*);
5385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } else if (c == 'c') {
5395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* character */
5405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* NOTE: char is promoted to int when passed through the stack */
5415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[0] = (char) va_arg(args, int);
5425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[1] = '\0';
5435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = buffer;
5445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } else if (c == 'p') {
545ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden            uint64_t  value = (uintptr_t) va_arg(args, void*);
5465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[0] = '0';
5475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[1] = 'x';
5485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            format_hex(buffer + 2, sizeof buffer-2, value, 0);
5495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = buffer;
5505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } else {
5515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* integers - first read value from stack */
5525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            uint64_t value;
5535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            int isSigned = (c == 'd' || c == 'i' || c == 'o');
5545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* NOTE: int8_t and int16_t are promoted to int when passed
5565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner             *       through the stack
5575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner             */
5585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            switch (bytelen) {
5595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 1: value = (uint8_t)  va_arg(args, int); break;
5605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 2: value = (uint16_t) va_arg(args, int); break;
5615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 4: value = va_arg(args, uint32_t); break;
5625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 8: value = va_arg(args, uint64_t); break;
5635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            default: return;  /* should not happen */
5645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* sign extension, if needed */
5675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (isSigned) {
5685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                int shift = 64 - 8*bytelen;
5695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                value = (uint64_t)(((int64_t)(value << shift)) >> shift);
5705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* format the number properly into our buffer */
5735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            switch (c) {
5745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 'i': case 'd':
5755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                format_integer(buffer, sizeof buffer, value, 10, isSigned);
5765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
5775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 'o':
5785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                format_integer(buffer, sizeof buffer, value, 8, isSigned);
5795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
5805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 'x': case 'X':
5815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                format_hex(buffer, sizeof buffer, value, (c == 'X'));
5825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
5835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            default:
5845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                buffer[0] = '\0';
5855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* then point to it */
5875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = buffer;
5885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* if we are here, 'str' points to the content that must be
5915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner         * outputted. handle padding and alignment now */
5925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        slen = strlen(str);
5945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (slen < width && !padLeft) {
5965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            char padChar = padZero ? '0' : ' ';
5975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            out_send_repeat(o, padChar, width - slen);
5985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        out_send(o, str, slen);
6015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (slen < width && padLeft) {
6035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            char padChar = padZero ? '0' : ' ';
6045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            out_send_repeat(o, padChar, width - slen);
6055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
6065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
6075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
6085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#ifdef UNIT_TESTS
6115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <stdio.h>
6135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int   gFails = 0;
6155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  MARGIN  40
6175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  UTEST_CHECK(condition,message) \
6195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
6205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (!(condition)) { \
6215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("KO\n"); \
6225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        gFails += 1; \
6235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    } else { \
6245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("ok\n"); \
6255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
6265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
6285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerutest_BufOut(void)
6295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
6305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char buffer[16];
6315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    BufOut bo[1];
6325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out* out;
6335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int ret;
6345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    buffer[0] = '1';
6365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = buf_out_init(bo, buffer, sizeof buffer);
6375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
6385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send(out, "abc", 3);
6395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
6405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send_repeat(out, 'X', 4);
6415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
6425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    buffer[sizeof buffer-1] = 'x';
6435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send_repeat(out, 'Y', 2*sizeof(buffer));
6445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
6455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = buf_out_init(bo, buffer, sizeof buffer);
6475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send_repeat(out, 'X', 2*sizeof(buffer));
6485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    ret = buf_out_length(bo);
6495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
6505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
6515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
6535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerutest_expect(const char*  result, const char*  format, ...)
6545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
6555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list args;
6565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    BufOut bo[1];
6575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char buffer[256];
6585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out* out = buf_out_init(bo, buffer, sizeof buffer);
6595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
6615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
6625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_vformat(out, format, args);
6635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
6645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (strcmp(result, buffer)) {
6665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("KO. got '%s' expecting '%s'\n", buffer, result);
6675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        gFails += 1;
6685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    } else {
6695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("ok. got '%s'\n", result);
6705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
6715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
6725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint  main(void)
6745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
6755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_BufOut();
6765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("", "");
6775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("a", "a");
6785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "01234", "");
6795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "%s", "01234");
6805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("aabbcc", "aa%scc", "bb");
6815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("a", "%c", 'a');
6825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("1234", "%d", 1234);
6835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("-8123", "%d", -8123);
6845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("16", "%hd", 0x7fff0010);
6855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("16", "%hhd", 0x7fffff10);
686ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("68719476736", "%lld", 0x1000000000LL);
6875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("70000", "%ld", 70000);
6885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("0xb0001234", "%p", (void*)0xb0001234);
6895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("12ab", "%x", 0x12ab);
6905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("12AB", "%X", 0x12ab);
6915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("00123456", "%08x", 0x123456);
6925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "0%d", 1234);
6935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect(" 1234", "%5d", 1234);
6945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "%05d", 1234);
6955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("    1234", "%8d", 1234);
6965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("1234    ", "%-8d", 1234);
6975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("abcdef     ", "%-11s", "abcdef");
6985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("something:1234", "%s:%d", "something", 1234);
699ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
700ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("5,0x0", "%d,%p", 5, NULL);
701ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
7025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return gFails != 0;
7035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
7045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
7055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* UNIT_TESTS */
706