od.c 10.5 KB
/*
 * Copyright (C) 2014 Martine Lenders <mlenders@inf.fu-berlin.de>
 *
 * This file is subject to the terms and conditions of the GNU Lesser
 * General Public License v2.1. See the file LICENSE in the top level
 * directory for more details.
 */

/**
 * @{
 *
 * @file
 */
#include <stdio.h>
#include <string.h>
#include <inttypes.h>

#include "od.h"

#define _OCTAL_BYTE_LENGTH  (3)
#define _INT_BYTE_LENGTH    (3)
#define _HEX_BYTE_LENGTH    (2)

static inline void _address_format(char *format, uint16_t flags)
{
    switch (flags & OD_FLAGS_ADDRESS_MASK) {
        case OD_FLAGS_ADDRESS_OCTAL:
            strncpy(format, "%09o", sizeof("%09o"));
            break;

        case OD_FLAGS_ADDRESS_HEX:
            strncpy(format, "%06x", sizeof("%06x"));
            break;

        case OD_FLAGS_ADDRESS_DECIMAL:
            strncpy(format, "%07d", sizeof("%07d"));
            break;

        default:
            break;
    }
}

static inline uint8_t _length(uint16_t flags)
{
    if (flags & OD_FLAGS_BYTES_CHAR) {
        return 1;
    }

    switch (flags & OD_FLAGS_LENGTH_MASK) {
        case OD_FLAGS_LENGTH_1:
            return 1;

        case OD_FLAGS_LENGTH_SHORT:
            return sizeof(short);

        case OD_FLAGS_LENGTH_2:
            return 2;

        case OD_FLAGS_LENGTH_LONG:
            return sizeof(long);

        case OD_FLAGS_LENGTH_8:
            return 8;

        case OD_FLAGS_LENGTH_4:
        default:
            return 4;
    }
}

static inline void _bytes_format(char *format, uint16_t flags)
{
    if (flags & OD_FLAGS_BYTES_CHAR) {
        strncpy(format, "    %c", sizeof("    %c"));
        return;
    }

    switch (flags & (OD_FLAGS_BYTES_MASK | OD_FLAGS_LENGTH_MASK)) {
        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_1:
            strncpy(format, " %03o", sizeof(" %03o"));
            break;

        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_2:
            strncpy(format, " %06" PRIo16, sizeof(" %06" PRIo16));
            break;

        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_4:
            strncpy(format, " %012" PRIo32, sizeof(" %012" PRIo32));
            break;

        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_8:
            strncpy(format, " %024" PRIo64, sizeof(" %024" PRIo64));
            break;

#if !defined(__MACH__)
        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%0%do", sizeof(short) * _OCTAL_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%0%dlo", sizeof(long) * _OCTAL_BYTE_LENGTH);
            break;
#else   /* !defined(__MACH__) */
        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %lu", sizeof(short) * _OCTAL_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %lu", sizeof(long) * _OCTAL_BYTE_LENGTH);
            break;
#endif  /* !defined(__MACH__) */

        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_1:
            strncpy(format, " %4d", sizeof(" %4d"));
            break;

        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_2:
            strncpy(format, " %6" PRId16, sizeof(" %6" PRId16));
            break;

        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_4:
            strncpy(format, " %12" PRId32, sizeof(" %12" PRId32));
            break;

        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_8:
            strncpy(format, " %24" PRId64, sizeof(" %24" PRId64));
            break;

#if !defined(__MACH__)
        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%%dd", sizeof(short) * _INT_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%%dld", sizeof(long) * _INT_BYTE_LENGTH);
            break;
#else   /* !defined(__MACH__) */
        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%%ld", sizeof(short) * _INT_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%%ld", sizeof(long) * _INT_BYTE_LENGTH);
            break;
#endif  /* !defined(__MACH__) */

        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_1:
            strncpy(format, " %3u", sizeof(" %3u"));
            break;

        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_2:
            strncpy(format, " %6" PRIu16, sizeof(" %6" PRIu16));
            break;

        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_4:
            strncpy(format, " %12" PRIu32, sizeof(" %12" PRIu32));
            break;

        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_8:
            strncpy(format, " %24" PRIu64, sizeof(" %24" PRIu64));
            break;

#if !defined(__MACH__)
        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%%uu", (unsigned)sizeof(short) * _INT_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%%ulu", sizeof(long) * _INT_BYTE_LENGTH);
            break;
#else   /* !defined(__MACH__) */
        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%%lu", sizeof(short) * _INT_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%%lu", sizeof(long) * _INT_BYTE_LENGTH);
            break;
#endif  /* !defined(__MACH__) */

        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_1:
            strncpy(format, " %02x", sizeof(" %02x"));
            break;

        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_2:
            strncpy(format, " %04" PRIx16, sizeof(" %04" PRIx16));
            break;

        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_4:
            strncpy(format, " %08" PRIx32, sizeof(" %08" PRIx32));
            break;

        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_8:
            strncpy(format, " %016" PRIx64, sizeof(" %016" PRIx64));
            break;

#if !defined(__MACH__)
        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%0%ux", (unsigned)sizeof(short) * _HEX_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%0%ulx", (unsigned)sizeof(long) * _HEX_BYTE_LENGTH);
            break;
#else   /* !defined(__MACH__) */
        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_SHORT:
            sprintf(format, " %%0%lx", sizeof(short) * _HEX_BYTE_LENGTH);
            break;

        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_LONG:
            sprintf(format, " %%0%lx", sizeof(long) * _HEX_BYTE_LENGTH);
            break;
#endif  /* !defined(__MACH__) */

        default:
            break;
    }
}

static void _print_date(const void *data, size_t offset, char *format, uint8_t length,
                        uint16_t flags)
{
    switch (length) {
        case 1:
            if (flags & OD_FLAGS_BYTES_CHAR) {
                switch (((signed char *)data)[offset]) {
                    case '\0':
                        printf("   \\0");
                        return;

                    case '\a':
                        printf("   \\a");
                        return;

                    case '\b':
                        printf("   \\b");
                        return;

                    case '\f':
                        printf("   \\f");
                        return;

                    case '\n':
                        printf("   \\n");
                        return;

                    case '\r':
                        printf("   \\r");
                        return;

                    case '\t':
                        printf("   \\t");
                        return;

                    case '\v':
                        printf("   \\v");
                        return;

                    default:
                        if (((signed char *)data)[offset] < 0) {
                            printf("  %03o", ((unsigned char *)data)[offset]);
                            return;
                        }
                        else if (((signed char *)data)[offset] < 32) {
                            printf("  %03o", ((char *)data)[offset]);
                            return;
                        }

                        break;
                }

            }

            if (flags & OD_FLAGS_BYTES_INT) {
                printf(format, ((int8_t *)data)[offset]);
            }
            else {
                printf(format, ((uint8_t *)data)[offset]);
            }

            break;

        case 2:
            if (flags & OD_FLAGS_BYTES_INT) {
                printf(format, ((int16_t *)data)[offset]);
            }
            else {
                printf(format, ((uint16_t *)data)[offset]);
            }

            break;

        case 4:
        default:
            if (flags & OD_FLAGS_BYTES_INT) {
                printf(format, ((int32_t *)data)[offset]);
            }
            else {
                printf(format, ((uint32_t *)data)[offset]);
            }

            break;

        case 8:
#ifndef MODULE_NEWLIB
            if (flags & OD_FLAGS_BYTES_INT) {
                printf(format, ((int64_t *)data)[offset]);
            }
            else {
                printf(format, ((uint64_t *)data)[offset]);
            }
#else
            printf("%s", format);
#endif

            break;

    }
}

static int _log10(uint8_t a)
{
    int res = 0;

    while (a > 0) {
        a /= 10;
        ++res;
    }

    return ++res;
}

void od(const void *data, size_t data_len, uint8_t width, uint16_t flags)
{
    char address_format[5];
    uint8_t date_length = _length(flags);
    char bytes_format[_log10(date_length) + 7];

    if (data_len == 0) {
        return;
    }

    _address_format(address_format, flags);
    _bytes_format(bytes_format, flags);

    if (width == 0) {
        width = OD_WIDTH_DEFAULT;
    }

    if (width < date_length) {
        width = 1;
    }
    else {
        width = (width / date_length);
    }

    if (data_len % date_length) {
        data_len = (data_len / date_length) + 1;
    }
    else {
        data_len = data_len / date_length;
    }

    if ((flags & OD_FLAGS_ADDRESS_MASK) != OD_FLAGS_ADDRESS_NONE) {
        printf(address_format, 0);
    }

    for (size_t i = 0; i < data_len; i++) {
        _print_date(data, i, bytes_format, date_length, flags);

        if ((((i + 1) % width) == 0) || i == (data_len - 1)) {
            printf("\n");

            if (i != (data_len - 1)) {
                if ((flags & OD_FLAGS_ADDRESS_MASK) != OD_FLAGS_ADDRESS_NONE) {
                    printf(address_format, date_length * (i + 1));
                }
            }
        }
    }
}

/** @} */