From 3519a3d9be3c578a9b98a921aa1545922ba7cd2e Mon Sep 17 00:00:00 2001
From: Martine Lenders <mail@martine-lenders.eu>
Date: Mon, 27 Oct 2014 19:03:04 +0100
Subject: [PATCH] sys: Initial import of analytical object dump (od) module

---
 sys/Makefile     |   3 +
 sys/include/od.h | 161 +++++++++++++++++++++++
 sys/od/Makefile  |   1 +
 sys/od/od.c      | 336 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 501 insertions(+)
 create mode 100644 sys/include/od.h
 create mode 100644 sys/od/Makefile
 create mode 100644 sys/od/od.c

diff --git a/sys/Makefile b/sys/Makefile
index ebbb3bd0c1..effe97398c 100644
--- a/sys/Makefile
+++ b/sys/Makefile
@@ -10,6 +10,9 @@ endif
 ifneq (,$(filter lib,$(USEMODULE)))
     DIRS += lib
 endif
+ifneq (,$(filter od,$(USEMODULE)))
+    DIRS += od
+endif
 ifneq (,$(filter ping,$(USEMODULE)))
     DIRS += ping
 endif
diff --git a/sys/include/od.h b/sys/include/od.h
new file mode 100644
index 0000000000..00dadf90f5
--- /dev/null
+++ b/sys/include/od.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014 Martin 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.
+ */
+
+/**
+ * @defgroup sys_od Object dump
+ * @ingroup  sys
+ * @brief   Allows to print out data dumps of memory regions in a similar fashion
+ *          to the UNIX's
+ *          <a href="http://pubs.opengroup.org/onlinepubs/9699919799/utilities/od.html">
+ *              od
+ *          </a> tool
+ *
+ * @see <a href="http://pubs.opengroup.org/onlinepubs/9699919799/utilities/od.html">
+ *          od(1)
+ *      </a>
+ * @{
+ *
+ * @file    od.h
+ *
+ * @author  Martine Lenders <mlenders@inf.fu-berlin.de>
+ */
+#ifndef __OD_H_
+#define __OD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/**
+ * @brief Bit-mask to extract address offset format settings from flags
+ */
+#define OD_FLAGS_ADDRESS_MASK   (0xc000)
+
+/**
+ * @brief Bit-mask to extract byte format settings from flags
+ */
+#define OD_FLAGS_BYTES_MASK     (0x3e00)
+
+/**
+ * @brief Bit-mask to extract length information for byte format from flags
+ */
+#define OD_FLAGS_LENGTH_MASK    (0x00f7)
+
+/**
+ * @anchor od_flags_address
+ * @name Address offset format flags
+ * @brief Flags to define format of the address offset
+ * @{
+ */
+#define OD_FLAGS_ADDRESS_OCTAL      (0x0000)    /**< octal address offset */
+#define OD_FLAGS_ADDRESS_HEX        (0x4000)    /**< hexadecimal address offset */
+#define OD_FLAGS_ADDRESS_DECIMAL    (0x8000)    /**< decimal address offset */
+#define OD_FLAGS_ADDRESS_NONE       (0xc000)    /**< no address offset */
+/** @} */
+
+/**
+ * @anchor od_flags_bytes
+ * @name Bytes format flags
+ * @brief Flags to define format of the byte output
+ * @{
+ */
+/**
+ * @brief Print `LENGTH` bytes as `LENGTH`-wide octal integer (`LENGTH` is defined
+ *        in the lower significant byte of the flags)
+ */
+#define OD_FLAGS_BYTES_OCTAL    (0x0000)
+
+/**
+ * @brief Print bytes as their represented character in ASCII
+ */
+#define OD_FLAGS_BYTES_CHAR     (0x2000)
+
+/**
+ * @brief Print `LENGTH` bytes as `LENGTH`-wide decimal integer (`LENGTH` is
+ *        defined in the lower significant byte of the flags)
+ */
+#define OD_FLAGS_BYTES_INT      (0x1000)
+
+/**
+ * @brief Alias for @ref OD_FLAGS_BYTES_INT
+ */
+#define OD_FLAGS_BYTES_DECIMAL  (OD_FLAGS_BYTES_INT)
+
+/* XXX: No float support for now, but reserved 0x0800 for this */
+
+/**
+ * @brief Print `LENGTH` bytes as `LENGTH`-wide decimal unsigned integer
+ *        (`LENGTH` is defined in the lower significant byte of the flags)
+ */
+#define OD_FLAGS_BYTES_UINT     (0x0400)
+
+/**
+ * @brief Print `LENGTH` bytes as `LENGTH`-wide hexadecimal integer
+ *        (`LENGTH` is defined in the lower significant byte of the flags)
+ */
+#define OD_FLAGS_BYTES_HEX      (0x0200)
+
+/** @} */
+
+
+/**
+ * @anchor od_flags_length
+ * @name Bytes format length flags
+ * @brief Flags to define format length of the byte output
+ * @{
+ */
+#define OD_FLAGS_LENGTH_1       (0x0010)    /**< 1 byte */
+#define OD_FLAGS_LENGTH_2       (0x0020)    /**< 2 byte */
+#define OD_FLAGS_LENGTH_4       (0x0000)    /**< 4 byte and default */
+#define OD_FLAGS_LENGTH_8       (0x0080)    /**< 8 byte */
+#define OD_FLAGS_LENGTH_CHAR    (OD_FLAGS_LENGTH_1)    /**< alias for OD_FLAGS_LENGTH_1 */
+#define OD_FLAGS_LENGTH_SHORT   (0x0002)    /**< sizeof(short) byte */
+#define OD_FLAGS_LENGTH_LONG    (0x0004)    /**< sizeof(long) byte */
+/** @} */
+
+/**
+ * @brief   Default value for parameter *width* of @ref od()
+ */
+#define OD_WIDTH_DEFAULT    (16)
+
+/**
+ * @brief Dumps memory stored at *data* up to *data_len* in octal, decimal, or
+ *        hexadecimal representation to stdout
+ *
+ * @param[in] data      Data to dump.
+ * @param[in] data_len  Length in bytes of *data* to output.
+ * @param[in] width     Number of bytes per line. If *width* is 0,
+ *                      @ref OD_WIDTH_DEFAULT is assumed as a default value.
+ * @param[in] flags     Flags as defined in @ref od_flags_address and
+ *                      @ref od_flags_bytes
+ */
+void od(void *data, size_t data_len, uint8_t width, uint16_t flags);
+
+/**
+ * @brief Dumps memory stored at *data* up to *data_len* in octal, decimal, or
+ *        hexadecimal representation to stdout with
+ *        `flags == OD_FLAGS_ADDRESS_HEX | OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_1`.
+ *
+ * @param[in] data      Data to dump.
+ * @param[in] data_len  Length in bytes of *data* to output.
+ * @param[in] width     Number of bytes per line. If *width* is 0,
+ *                      @ref OD_WIDTH_DEFAULT is assumed as a default value.
+ */
+static inline void od_hex_dump(void *data, size_t data_len, uint8_t width)
+{
+    od(data, data_len, width, OD_FLAGS_ADDRESS_HEX | OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_1);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __OD_H_ */
+/** @} */
diff --git a/sys/od/Makefile b/sys/od/Makefile
new file mode 100644
index 0000000000..48422e909a
--- /dev/null
+++ b/sys/od/Makefile
@@ -0,0 +1 @@
+include $(RIOTBASE)/Makefile.base
diff --git a/sys/od/od.c b/sys/od/od.c
new file mode 100644
index 0000000000..659ea3dff3
--- /dev/null
+++ b/sys/od/od.c
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2014 Martin 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    od.c
+ */
+#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, " %03" PRIo8, sizeof(" %03" PRIo8));
+            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;
+
+        case OD_FLAGS_BYTES_OCTAL | OD_FLAGS_LENGTH_SHORT:
+            sprintf(format, " %%0%dho", 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;
+
+        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_1:
+            strncpy(format, " %4" PRId8, sizeof(" %4" PRId8));
+            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;
+
+        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_SHORT:
+            sprintf(format, " %%%dhd", sizeof(short) * _INT_BYTE_LENGTH);
+            break;
+
+        case OD_FLAGS_BYTES_INT | OD_FLAGS_LENGTH_LONG:
+            sprintf(format, " %%%dld", sizeof(long) * _INT_BYTE_LENGTH);
+            break;
+
+        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_1:
+            strncpy(format, " %3" PRIu8, sizeof(" %3" PRIu8));
+            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;
+
+        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_SHORT:
+            sprintf(format, " %%%dhu", sizeof(short) * _INT_BYTE_LENGTH);
+            break;
+
+        case OD_FLAGS_BYTES_UINT | OD_FLAGS_LENGTH_LONG:
+            sprintf(format, " %%%dlu", sizeof(long) * _INT_BYTE_LENGTH);
+            break;
+
+        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_1:
+            strncpy(format, " %02" PRIx8, sizeof(" %02" PRIx8));
+            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;
+
+        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_SHORT:
+            sprintf(format, " %%0%dhx", sizeof(short) * _HEX_BYTE_LENGTH);
+            break;
+
+        case OD_FLAGS_BYTES_HEX | OD_FLAGS_LENGTH_LONG:
+            sprintf(format, " %%0%dlx", sizeof(long) * _HEX_BYTE_LENGTH);
+            break;
+
+        default:
+            break;
+    }
+}
+
+static void _print_date(void *data, size_t offset, char *format, uint8_t length,
+                        uint16_t flags)
+{
+    switch (length) {
+        case 1:
+            if (flags & OD_FLAGS_BYTES_CHAR) {
+                switch (((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 (((char *)data)[offset] < 0) {
+                            printf("  %03o", ((unsigned char *)data)[offset]);
+                            return;
+                        }
+                        else if (((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:
+            if (flags & OD_FLAGS_BYTES_INT) {
+                printf(format, ((int64_t *)data)[offset]);
+            }
+            else {
+                printf(format, ((uint64_t *)data)[offset]);
+            }
+
+            break;
+
+    }
+}
+
+static int _log10(uint8_t a)
+{
+    int res = 0;
+
+    while (a > 0) {
+        a /= 10;
+        ++res;
+    }
+
+    return ++res;
+}
+
+void od(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];
+
+    _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));
+                }
+            }
+        }
+    }
+}
+
+/** @} */
-- 
GitLab