/*
**  OSSP l2 - Flexible Logging
**  Copyright (c) 2001-2003 Cable & Wireless Deutschland GmbH
**  Copyright (c) 2001-2003 The OSSP Project (http://www.ossp.org/)
**  Copyright (c) 2001-2003 Ralf S. Engelschall <rse@engelschall.com>
**
**  This file is part of OSSP l2, a flexible logging library which
**  can be found at http://www.ossp.org/pkg/lib/l2/.
**
**  Permission to use, copy, modify, and distribute this software for
**  any purpose with or without fee is hereby granted, provided that
**  the above copyright notice and this permission notice appear in all
**  copies.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  l2_format.c: extra formatters
*/

#include <ctype.h>

#include "l2.h"
#include <errno.h>

l2_result_t
l2_util_fmt_string(
    l2_context_t *ctx, const char id, const char *param,
    char *bufptr, size_t bufsize, size_t *buflen, va_list *ap)
{
    char *cpStr;
    size_t nStr;

    cpStr = va_arg(*ap, char *);
    nStr  = (size_t)va_arg(*ap, int);
    if (cpStr == NULL)
        return L2_ERR_ARG;
    if (nStr > bufsize)
        return L2_ERR_MEM;
    memcpy(bufptr, cpStr, nStr);
    *buflen = nStr;
    return L2_OK;
}

l2_result_t
l2_util_fmt_dump(
    l2_context_t *ctx, const char id, const char *param,
    char *bufptr, size_t bufsize, size_t *buflen, va_list *ap)
{
    static const char hex_table[]    = "0123456789abcdef";
    static const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                                       "abcdefghijklmnopqrstuvwxyz"
                                       "0123456789+/";
    unsigned char base64_in[3];
    unsigned char base64_out[4];
    size_t nData;
    unsigned char *ucpData;
    unsigned char uc;
    unsigned char *cpO;
    unsigned char *cpI;
    int i;
    int n;

    /* fetch data pointer and data size from argument list */
    ucpData = va_arg(*ap, unsigned char *);
    nData   = (size_t)va_arg(*ap, int);

    /* argument sanity check */
    if (ucpData == NULL)
        return L2_ERR_ARG;

    /*
     * textual dumping: "foo\1bar" -> "foo\x01bar"
     */
    if (strcmp(param, "text") == 0 || param[0] == '\0') {
        cpI = ucpData;
        cpO = (unsigned char *)bufptr;
        while (cpI < (ucpData+nData)) {
            uc = *cpI++;
            if (isprint((int)uc)) {
                if ((char *)cpO >= (bufptr+bufsize))
                    return L2_ERR_MEM;
                *cpO++ = uc;
            }
            else if (uc == '\\') {
                if ((char *)(cpO+1) >= (bufptr+bufsize))
                    return L2_ERR_MEM;
                *cpO++ = '\\';
                *cpO++ = '\\';
            }
            else if (uc == '\t' || uc == '\r' || uc == '\n') {
                if ((char *)(cpO+1) >= (bufptr+bufsize))
                    return L2_ERR_MEM;
                *cpO++ = '\\';
                switch ((int)uc) {
                    case '\t': *cpO++ = 't'; break;
                    case '\r': *cpO++ = 'r'; break;
                    case '\n': *cpO++ = 'n'; break;
                    default: break;
                }
            }
            else {
                if ((char *)(cpO+3) >= (bufptr+bufsize))
                    return L2_ERR_MEM;
                *cpO++ = '\\';
                *cpO++ = 'x';
                *cpO++ = hex_table[(uc >> 4) & 0xf];
                *cpO++ = hex_table[uc & 0xf];
            }
        }
        *buflen = ((char *)cpO - bufptr);
    }

    /*
     * hexadecimal dumping: "foo\1bar" -> "66:6f:6f:01:62:61:72"
     */
    else if (strcmp(param, "hex") == 0) {
        if (((nData * 3) - 1) > bufsize)
            return L2_ERR_MEM;
        cpO = (unsigned char *)bufptr;
        for (i = 0; i < nData; i++) {
            uc = *ucpData++;
            *cpO++ = hex_table[(uc >> 4) & 0xf];
            *cpO++ = hex_table[uc & 0xf];
            if (i < (nData - 1))
                *cpO++ = ':';
        }
        *buflen = (nData * 3) - 1;
    }

    /*
     * Base64 dumping: "foo\1bar" -> "Zm9vAWJhcg=="
     */
    else if (strcmp(param, "base64") == 0) {
        /* bulk encoding */
        n = 0;
        while (nData >= 3) {
            base64_in[0] = *ucpData++;
            base64_in[1] = *ucpData++;
            base64_in[2] = *ucpData++;
            nData -= 3;
            base64_out[0] = ( base64_in[0] >> 2);
            base64_out[1] = ((base64_in[0] & 0x03) << 4) + (base64_in[1] >> 4);
            base64_out[2] = ((base64_in[1] & 0x0f) << 2) + (base64_in[2] >> 6);
            base64_out[3] = ( base64_in[2] & 0x3f);
            if (n + 4 > bufsize)
                return L2_ERR_MEM;
            bufptr[n++] = base64_table[base64_out[0]];
            bufptr[n++] = base64_table[base64_out[1]];
            bufptr[n++] = base64_table[base64_out[2]];
            bufptr[n++] = base64_table[base64_out[3]];
        }
        /* now worry about padding with remaining 1 or 2 bytes */
        if (nData != 0) {
            base64_in[0] = base64_in[1] = base64_in[2] = '\0';
            for (i = 0; i < nData; i++)
                base64_in[i] = *ucpData++;
            base64_out[0] = ( base64_in[0] >> 2);
            base64_out[1] = ((base64_in[0] & 0x03) << 4) + (base64_in[1] >> 4);
            base64_out[2] = ((base64_in[1] & 0x0f) << 2) + (base64_in[2] >> 6);
            if (n + 4 > bufsize)
                return L2_ERR_MEM;
            bufptr[n++] = base64_table[base64_out[0]];
            bufptr[n++] = base64_table[base64_out[1]];
            if (nData == 1)
                bufptr[n++] = '=';
            else
                bufptr[n++] = base64_table[base64_out[2]];
            bufptr[n++] = '=';
        }
        if (n >= bufsize)
            return L2_ERR_MEM;
        *buflen = n;
    }

    /* anything else is an unknown dumping method... */
    else
        return L2_ERR_ARG;

    return L2_OK;
}

l2_result_t
l2_util_fmt_errno(
    l2_context_t *ctx, const char id, const char *param,
    char *bufptr, size_t bufsize, size_t *buflen, va_list *ap)
{
    sprintf(bufptr, "(%d) %s", errno, strerror(errno));
    *buflen = strlen(bufptr);
    return L2_OK;
}
