#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libgen.h>
#include <errno.h>
#include <glib.h>
#include <sys/stat.h> /* mkdir */

#include "stdio_util.h"

/* Function with behaviour like `mkdir -p'  */
int
stdiou_mkpath (const char *s, mode_t mode)
{
    char *q, *r = NULL, *path = NULL, *up = NULL;
    int rv;
 
    rv = -1;
    if (strcmp (s, ".") == 0 || strcmp (s, "/") == 0)
        return (0);
 
    if ((path = strdup (s)) == NULL)
        exit (EXIT_SUCCESS);
 
    if ((q = strdup(s)) == NULL)
        exit (EXIT_SUCCESS);
 
    if ((r = dirname (q)) == NULL)
        goto out;
 
    if ((up = strdup (r)) == NULL)
        exit (EXIT_SUCCESS);
 
    if ((stdiou_mkpath (up, mode) == -1) && (errno != EEXIST))
        goto out;
 
    if ((mkdir (path, mode) == -1) && (errno != EEXIST))
        rv = -1;
    else
        rv = 0;
 
  out:
    if (up != NULL)
        free (up);
    free (q);
    free (path);
    return (rv);
}



/* prints buf as a hexadecimal stream in big-endian order */
int
stdiou_shexdump (char *str, const void *buf, size_t len, const char *prefix)
{
    char *sptr = str;
    if (prefix)
        sptr += sprintf (sptr, "%s", prefix);
    else
        sptr += sprintf (sptr, "0x");

    if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
        const unsigned char *bptr = buf+len-1;
        for (int i=0; i<len; i++, bptr--)
            sptr += sprintf (sptr, "%02x", *bptr);
    }
    else { /* G_BIG_ENDIAN */
        const unsigned char *bptr = buf;
        for (int i=0; i<len; i++, bptr++)
            sptr += sprintf (sptr, "%02x", *bptr);
    }
    return sptr-str;
}

int
stdiou_hexdump (const void *buf, size_t len, const char *prefix)
{
    size_t plen = 2; // "0x" default
    if (prefix)
        plen = strlen (prefix);
    char str[2*len+plen+1]; // '\0' terminator
    stdiou_shexdump (str, buf, len, prefix);
    return printf ("%s", str);
}

int
stdiou_fhexdump (FILE *stream, const void *buf, size_t len, const char *prefix)
{
    size_t plen = 2;
    if (prefix)
        plen = strlen (prefix);
    char str[2*len+plen+1];
    stdiou_shexdump (str, buf, len, prefix);
    return fprintf (stream, "%s", str);
}

/* prints buf as a bit stream in big-endian order */
int
stdiou_sbindump (char *str, const void *buf, size_t len, const char *prefix)
{
    char *sptr = str;
    if (prefix)
        sptr += sprintf (sptr, "%s", prefix);
    else
        sptr += sprintf (sptr, "0b");

    if (G_BYTE_ORDER == G_LITTLE_ENDIAN) {
        const unsigned char *bptr = buf+len-1;
        for (int i=0; i<len; i++, bptr--)
            for (int b=7; b>=0; b--)
                sptr += sprintf (sptr, "%d", (*bptr & (1<<b)) ? 1 : 0 );
    }
    else { /* G_BIG_ENDIAN */
        const unsigned char *bptr = buf;
        for (size_t i=0; i<len; i++, bptr++)
            sptr += sprintf (sptr, "%d", *bptr);
    }
    return sptr-str;
}

int
stdiou_bindump (const void *buf, size_t len, const char *prefix)
{
    size_t plen = 2; // "0b" default
    if (prefix)
        plen = strlen (prefix);
    char str[8*len+plen+1]; // '\0' terminator
    stdiou_sbindump (str, buf, len, prefix);
    return printf ("%s", str);
}

int
stdiou_fbindump (FILE *stream, const void *buf, size_t len, const char *prefix)
{
    size_t plen = 2;
    if (prefix)
        plen = strlen (prefix);
    char str[8*len+plen+1];
    stdiou_sbindump (str, buf, len, prefix);
    return fprintf (stream, "%s", str);
}
