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
175166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner/* The pthread implementation uses snprintf(). If we define it here, we
176166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner * avoid pulling the stdio vfprintf() implementation into the linker
177166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner * saving about 19KB of machine code.
178166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner */
179166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turnerint
180166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turnersnprintf(char* buff, size_t bufsize, const char* format, ...)
181166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner{
182166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner    va_list args;
183166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner    int ret;
184166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner    va_start(args, format);
185166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner    ret = vsnprintf(buff, bufsize, format, args);
186166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner    va_end(args);
187166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner    return ret;
188166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner}
189166b7dbd4aa143fc22f61c64dae1219a910f1a6eDavid 'Digit' Turner
1905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#if LINKER_DEBUG
1915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#if !LINKER_DEBUG_TO_LOG
1935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** File descriptor output
1955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
1965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
1975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnertypedef struct {
1985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out out[1];
1995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int fd;
2005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int total;
2015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner} FdOut;
2025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
2045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerfd_out_send(void *opaque, const char *data, int len)
2055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    FdOut *fdo = opaque;
2075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (len < 0)
2095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        len = strlen(data);
2105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (len > 0) {
2125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int ret = write(fdo->fd, data, len);
2135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (ret < 0) {
2145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (errno == EINTR)
2155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
2165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
2175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
2185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        data += ret;
2195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        len -= ret;
2205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        fdo->total += ret;
2215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
2225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic Out*
2255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerfd_out_init(FdOut *fdo, int  fd)
2265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->out->opaque = fdo;
2285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->out->send = fd_out_send;
2295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->fd = fd;
2305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    fdo->total = 0;
2315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return fdo->out;
2335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int
2365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerfd_out_length(FdOut *fdo)
2375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return fdo->total;
2395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint
2435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_fd(int fd, const char *format, ...)
2445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    FdOut fdo;
2465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out* out;
2475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list args;
2485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = fd_out_init(&fdo, fd);
2505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (out == NULL)
2515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        return 0;
2525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
2545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_vformat(out, format, args);
2555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
2565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return fd_out_length(&fdo);
2585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
2595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#else /* LINKER_DEBUG_TO_LOG */
2615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** Log output
2635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
2645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* We need our own version of __libc_android_log_vprint, otherwise
2665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * the log output is completely broken. Probably due to the fact
2675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * that the C library is not initialized yet.
2685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
2695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * You can test that by setting CUSTOM_LOG_VPRINT to 0
2705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
2715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  CUSTOM_LOG_VPRINT  1
2725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#if CUSTOM_LOG_VPRINT
2745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <unistd.h>
2765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <fcntl.h>
2775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <sys/uio.h>
2785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int log_vprint(int prio, const char *tag, const char *fmt, va_list  args)
2805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
2815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char buf[1024];
2825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int result;
2835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    static int log_fd = -1;
2845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    result = vformat_buffer(buf, sizeof buf, fmt, args);
2865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (log_fd < 0) {
2885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        log_fd = open("/dev/log/main", O_WRONLY);
2895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (log_fd < 0)
2905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            return result;
2915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
2925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    {
2945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        ssize_t ret;
2955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        struct iovec vec[3];
2965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
2975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[0].iov_base = (unsigned char *) &prio;
2985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[0].iov_len = 1;
2995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[1].iov_base = (void *) tag;
3005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[1].iov_len = strlen(tag) + 1;
3015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[2].iov_base = (void *) buf;
3025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        vec[2].iov_len = strlen(buf) + 1;
3035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        do {
3055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            ret = writev(log_fd, vec, 3);
3065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } while ((ret < 0) && (errno == EINTR));
3075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return result;
3095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
3105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  __libc_android_log_vprint  log_vprint
3125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#else /* !CUSTOM_LOG_VPRINT */
3145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3154688279db5dcc4004941e7f133c4a1c3617d842cElliott Hughesextern "C" int __libc_android_log_vprint(int  prio, const char* tag, const char*  format, va_list ap);
3165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* !CUSTOM_LOG_VPRINT */
3185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint
3205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_log(int prio, const char *tag, const char *format, ...)
3215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int ret;
3235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list  args;
3245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
3255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    ret = __libc_android_log_vprint(prio, tag, format, args);
3265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
3275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return ret;
3285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
3295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* LINKER_DEBUG_TO_LOG */
3315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* LINKER_DEBUG */
3335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/*** formatted output implementation
3355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner ***/
3365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Parse a decimal string from 'format + *ppos',
3385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * return the value, and writes the new position past
3395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * the decimal string in '*ppos' on exit.
3405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner *
3415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * NOTE: Does *not* handle a sign prefix.
3425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
3435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic unsigned
3445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerparse_decimal(const char *format, int *ppos)
3455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    const char* p = format + *ppos;
3475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    unsigned result = 0;
3485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    for (;;) {
3505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int ch = *p;
3515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        unsigned d = (unsigned)(ch - '0');
3525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (d >= 10U)
3545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
3555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        result = result*10 + d;
3575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        p++;
3585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    *ppos = p - format;
3605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return result;
3615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
3625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* write an octal/decimal/number into a bounded buffer.
3645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * assumes that bufsize > 0, and 'digits' is a string of
3655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * digits of at least 'base' values.
3665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner */
3675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
3685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_number(char *buffer, size_t bufsize, uint64_t value, int base, const char *digits)
3695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
3705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *pos = buffer;
3715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char *end = buffer + bufsize - 1;
3725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    /* generate digit string in reverse order */
3745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (value) {
3755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        unsigned d = value % base;
3765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        value /= base;
3775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (pos < end) {
3785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            *pos++ = digits[d];
3795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
3805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    /* special case for 0 */
3835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (pos == buffer) {
3845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (pos < end) {
3855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            *pos++ = '0';
3865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
3875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
3885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    pos[0] = '\0';
3895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
3905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    /* now reverse digit string in-place */
3915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    end = pos - 1;
3925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    pos = buffer;
3935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    while (pos < end) {
3945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        int ch = pos[0];
3955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        pos[0] = end[0];
3965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        end[0] = (char) ch;
3975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        pos++;
3985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        end--;
3995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
4005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Write an integer (octal or decimal) into a buffer, assumes buffsize > 2 */
4035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_integer(char *buffer, size_t buffsize, uint64_t value, int base, int isSigned)
4055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
4065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (isSigned && (int64_t)value < 0) {
4075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        buffer[0] = '-';
4085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        buffer += 1;
4095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        buffsize -= 1;
4105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        value = (uint64_t)(-(int64_t)value);
4115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
4125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    format_number(buffer, buffsize, value, base, "0123456789");
4145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Write an hexadecimal into a buffer, isCap is true for capital alphas.
4175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner * Assumes bufsize > 2 */
4185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerformat_hex(char *buffer, size_t buffsize, uint64_t value, int isCap)
4205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
4215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    const char *digits = isCap ? "0123456789ABCDEF" : "0123456789abcdef";
4225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    format_number(buffer, buffsize, value, 16, digits);
4245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
4255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner/* Perform formatted output to an output target 'o' */
4285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
4295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerout_vformat(Out *o, const char *format, va_list args)
4305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
431ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    int nn = 0;
4325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    for (;;) {
434ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int mm;
435ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int padZero = 0;
436ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int padLeft = 0;
437ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        char sign = '\0';
438ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int width = -1;
439ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int prec  = -1;
440ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        size_t bytelen = sizeof(int);
441ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        const char*  str;
442ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        int slen;
443ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden        char buffer[32];  /* temporary buffer used to format numbers */
444ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden
4455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        char  c;
4465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* first, find all characters that are not 0 or '%' */
4485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* then send them to the output directly */
4495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        mm = nn;
4505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        do {
4515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[mm];
4525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (c == '\0' || c == '%')
4535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
4545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            mm++;
4555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } while (1);
4565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (mm > nn) {
4585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            out_send(o, format+nn, mm-nn);
4595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            nn = mm;
4605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
4615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* is this it ? then exit */
4635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (c == '\0')
4645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
4655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* nope, we are at a '%' modifier */
4675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        nn++;  // skip it
4685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* parse flags */
4705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        for (;;) {
4715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
4725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (c == '\0') {  /* single trailing '%' ? */
4735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                c = '%';
4745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                out_send(o, &c, 1);
4755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                return;
4765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            else if (c == '0') {
4785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                padZero = 1;
4795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
4805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            else if (c == '-') {
4825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                padLeft = 1;
4835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
4845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            else if (c == ' ' || c == '+') {
4865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                sign = c;
4875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                continue;
4885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
4895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
4905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
4915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* parse field width */
4935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if ((c >= '0' && c <= '9')) {
4945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            nn --;
4955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            width = (int)parse_decimal(format, &nn);
4965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
4975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
4985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
4995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* parse precision */
5005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (c == '.') {
5015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            prec = (int)parse_decimal(format, &nn);
5025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* length modifier */
5065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        switch (c) {
5075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 'h':
5085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(short);
5095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (format[nn] == 'h') {
5105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                bytelen = sizeof(char);
5115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                nn += 1;
5125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 'l':
5165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(long);
5175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (format[nn] == 'l') {
5185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                bytelen = sizeof(long long);
5195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                nn += 1;
5205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 'z':
5245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(size_t);
5255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        case 't':
5285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            bytelen = sizeof(ptrdiff_t);
5295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            c = format[nn++];
5305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            break;
5315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        default:
5325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            ;
5335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* conversion specifier */
5365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (c == 's') {
5375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* string */
5385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = va_arg(args, const char*);
5395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } else if (c == 'c') {
5405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* character */
5415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* NOTE: char is promoted to int when passed through the stack */
5425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[0] = (char) va_arg(args, int);
5435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[1] = '\0';
5445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = buffer;
5455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } else if (c == 'p') {
546ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden            uint64_t  value = (uintptr_t) va_arg(args, void*);
5475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[0] = '0';
5485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            buffer[1] = 'x';
5495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            format_hex(buffer + 2, sizeof buffer-2, value, 0);
5505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = buffer;
5515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        } else {
5525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* integers - first read value from stack */
5535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            uint64_t value;
5545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            int isSigned = (c == 'd' || c == 'i' || c == 'o');
5555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* NOTE: int8_t and int16_t are promoted to int when passed
5575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner             *       through the stack
5585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner             */
5595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            switch (bytelen) {
5605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 1: value = (uint8_t)  va_arg(args, int); break;
5615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 2: value = (uint16_t) va_arg(args, int); break;
5625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 4: value = va_arg(args, uint32_t); break;
5635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 8: value = va_arg(args, uint64_t); break;
5645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            default: return;  /* should not happen */
5655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* sign extension, if needed */
5685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            if (isSigned) {
5695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                int shift = 64 - 8*bytelen;
5705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                value = (uint64_t)(((int64_t)(value << shift)) >> shift);
5715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* format the number properly into our buffer */
5745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            switch (c) {
5755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 'i': case 'd':
5765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                format_integer(buffer, sizeof buffer, value, 10, isSigned);
5775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
5785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 'o':
5795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                format_integer(buffer, sizeof buffer, value, 8, isSigned);
5805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
5815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            case 'x': case 'X':
5825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                format_hex(buffer, sizeof buffer, value, (c == 'X'));
5835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                break;
5845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            default:
5855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner                buffer[0] = '\0';
5865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            }
5875c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            /* then point to it */
5885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            str = buffer;
5895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
5905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        /* if we are here, 'str' points to the content that must be
5925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner         * outputted. handle padding and alignment now */
5935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        slen = strlen(str);
5955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
5965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (slen < width && !padLeft) {
5975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            char padChar = padZero ? '0' : ' ';
5985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            out_send_repeat(o, padChar, width - slen);
5995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
6005c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6015c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        out_send(o, str, slen);
6025c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        if (slen < width && padLeft) {
6045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            char padChar = padZero ? '0' : ' ';
6055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner            out_send_repeat(o, padChar, width - slen);
6065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        }
6075c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
6085c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
6095c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6105c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6115c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#ifdef UNIT_TESTS
6125c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6135c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#include <stdio.h>
6145c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6155c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic int   gFails = 0;
6165c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6175c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  MARGIN  40
6185c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6195c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#define  UTEST_CHECK(condition,message) \
6205c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    printf("Checking %-*s: ", MARGIN, message); fflush(stdout); \
6215c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (!(condition)) { \
6225c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("KO\n"); \
6235c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        gFails += 1; \
6245c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    } else { \
6255c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("ok\n"); \
6265c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
6275c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6285c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
6295c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerutest_BufOut(void)
6305c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
6315c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char buffer[16];
6325c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    BufOut bo[1];
6335c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out* out;
6345c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    int ret;
6355c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6365c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    buffer[0] = '1';
6375c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = buf_out_init(bo, buffer, sizeof buffer);
6385c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(buffer[0] == '\0', "buf_out_init clears initial byte");
6395c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send(out, "abc", 3);
6405c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(!memcmp(buffer, "abc", 4), "out_send() works with BufOut");
6415c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send_repeat(out, 'X', 4);
6425c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(!memcmp(buffer, "abcXXXX", 8), "out_send_repeat() works with BufOut");
6435c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    buffer[sizeof buffer-1] = 'x';
6445c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send_repeat(out, 'Y', 2*sizeof(buffer));
6455c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(buffer[sizeof buffer-1] == '\0', "overflows always zero-terminates");
6465c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6475c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out = buf_out_init(bo, buffer, sizeof buffer);
6485c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_send_repeat(out, 'X', 2*sizeof(buffer));
6495c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    ret = buf_out_length(bo);
6505c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    UTEST_CHECK(ret == 2*sizeof(buffer), "correct size returned on overflow");
6515c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
6525c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6535c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerstatic void
6545c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerutest_expect(const char*  result, const char*  format, ...)
6555c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
6565c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_list args;
6575c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    BufOut bo[1];
6585c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    char buffer[256];
6595c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    Out* out = buf_out_init(bo, buffer, sizeof buffer);
6605c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6615c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    printf("Checking %-*s: ", MARGIN, format); fflush(stdout);
6625c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_start(args, format);
6635c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    out_vformat(out, format, args);
6645c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    va_end(args);
6655c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6665c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    if (strcmp(result, buffer)) {
6675c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("KO. got '%s' expecting '%s'\n", buffer, result);
6685c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        gFails += 1;
6695c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    } else {
6705c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner        printf("ok. got '%s'\n", result);
6715c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    }
6725c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
6735c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
6745c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turnerint  main(void)
6755c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner{
6765c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_BufOut();
6775c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("", "");
6785c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("a", "a");
6795c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "01234", "");
6805c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "%s", "01234");
6815c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("aabbcc", "aa%scc", "bb");
6825c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("a", "%c", 'a');
6835c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("1234", "%d", 1234);
6845c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("-8123", "%d", -8123);
6855c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("16", "%hd", 0x7fff0010);
6865c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("16", "%hhd", 0x7fffff10);
687ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("68719476736", "%lld", 0x1000000000LL);
6885c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("70000", "%ld", 70000);
6895c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("0xb0001234", "%p", (void*)0xb0001234);
6905c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("12ab", "%x", 0x12ab);
6915c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("12AB", "%X", 0x12ab);
6925c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("00123456", "%08x", 0x123456);
6935c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "0%d", 1234);
6945c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect(" 1234", "%5d", 1234);
6955c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("01234", "%05d", 1234);
6965c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("    1234", "%8d", 1234);
6975c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("1234    ", "%-8d", 1234);
6985c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("abcdef     ", "%-11s", "abcdef");
6995c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    utest_expect("something:1234", "%s:%d", "something", 1234);
700ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("005:5:05", "%03d:%d:%02d", 5, 5, 5);
701ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("5,0x0", "%d,%p", 5, NULL);
702ec92af8fe5d28c74f3505932135b1b8f3fbaad00Andy McFadden    utest_expect("68719476736,6,7,8", "%lld,%d,%d,%d", 0x1000000000LL, 6, 7, 8);
7035c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner    return gFails != 0;
7045c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner}
7055c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner
7065c734644eebf8d01be1e86cbe20a111a5c5a2738David 'Digit' Turner#endif /* UNIT_TESTS */
707