/*
 * lib.c
 * Global utility functions.
 *
 * Copyright (c) 2003-2006 Christoph Pfisterer
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use, copy,
 * modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

#include "global.h"
#include <stdarg.h>

/*
 * output functions
 */

#define LEVELS (8)

static const char *insets[LEVELS] = {
  "",
  "  ",
  "    ",
  "      ",
  "        ",
  "          ",
  "            ",
  "              ",
};
static char line_akku[4096];

void print_line(int level, const char *fmt, ...)
{
  va_list par;

  va_start(par, fmt);
  vsnprintf(line_akku, 4096, fmt, par);
  va_end(par);

  if (level >= LEVELS)
    bailout("Recursion loop caught");
  printf("%s%s\n", insets[level], line_akku);
}

void start_line(const char *fmt, ...)
{
  va_list par;

  va_start(par, fmt);
  vsnprintf(line_akku, 4096, fmt, par);
  va_end(par);
}

void continue_line(const char *fmt, ...)
{
  va_list par;
  int len = strlen(line_akku);

  va_start(par, fmt);
  vsnprintf(line_akku + len, 4096 - len, fmt, par);
  va_end(par);
}

void finish_line(int level)
{
  if (level >= LEVELS)
    bailout("Recursion loop caught");
  printf("%s%s\n", insets[level], line_akku);
}

/*
 * formatting functions
 */

/* TODO: make all of this safe from buffer overruns... */

/*
 * format_raw_size() does the actual unit-suffix formatting.
 *
 * Returns an indicator for the format used:
 *  0 - rounded to some unit
 *  1 - whole multiple of some unit (return code limited to KiB)
 *  2 - plain bytes
 */

static int format_raw_size(char *buf, u8 size)
{
  int unit_index, dd;
  u8 unit_size, card;
  const char *unit_names[] =
    { "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", NULL };
  const int dd_mult[4] = { 1, 10, 100, 1000 };

  /* just a few bytes */
  if (size < 1024) {
    sprintf(buf, "%llu bytes", size);
    return 2;
  }

  /* find a suitable unit */
  for (unit_index = 0, unit_size = 1024;
       unit_names[unit_index] != NULL;
       unit_index++, unit_size <<= 10) {

    /* size is at least one of the next unit -> use that */
    if (size >= 1024 * unit_size)
      continue;

    /* check integral multiples */
    if ((size % unit_size) == 0) {
      card = size / unit_size;
      sprintf(buf, "%d %s",
	      (int)card,
	      unit_names[unit_index]);
      return unit_index ? 0 : 1;
    }

    /* find suitable number of decimal digits */
    for (dd = 3; dd >= 1; dd--) {
      card = (size * dd_mult[dd] + (unit_size >> 1)) / unit_size;
      if (card >= 10000)
	continue;  /* more than four significant digits */

      sprintf(buf, "%d.%0*d %s",
	          (int)(card / dd_mult[dd]),
	      dd, (int)(card % dd_mult[dd]),
	      unit_names[unit_index]);
      return 0;
    }
  }

  /* fallback (something wrong with the numbers?) */
  strcpy(buf, "off the scale");
  return 0;
}

void format_blocky_size(char *buf, u8 count, u4 blocksize,
			const char *blockname, const char *append)
{
  int used;
  u8 total_size;
  char *p;
  char blocksizebuf[32];

  total_size = count * blocksize;
  used = format_raw_size(buf, total_size);
  p = strchr(buf, 0);

  *p++ = ' ';
  *p++ = '(';

  if (used != 2) {
    sprintf(p, "%llu bytes, ", total_size);
    p = strchr(buf, 0);
  }

  if (blocksize == 512 && strcmp(blockname, "sectors") == 0) {
    sprintf(p, "%llu %s", count, blockname);
  } else {
    if (blocksize < 64*1024 && (blocksize % 1024) != 0)
      sprintf(blocksizebuf, "%lu bytes", blocksize);
    else
      format_raw_size(blocksizebuf, blocksize);
    sprintf(p, "%llu %s of %s", count, blockname, blocksizebuf);
  }
  p = strchr(buf, 0);

  if (append != NULL) {
    strcpy(p, append);
    p = strchr(buf, 0);
  }

  *p++ = ')';
  *p++ = 0;
}

void format_size(char *buf, u8 size)
{
  int used;

  used = format_raw_size(buf, size);
  if (used > 0)
    return;

  sprintf(strchr(buf, 0), " (%llu bytes)", size);
}

void format_size_verbose(char *buf, u8 size)
{
  int used;

  used = format_raw_size(buf, size);
  if (used == 2)
    return;

  sprintf(strchr(buf, 0), " (%llu bytes)", size);
}

void format_ascii(void *from, char *to)
{
  u1 *p = (u1 *)from;
  u1 *q = (u1 *)to;
  int c;

  while ((c = *p++)) {
    if (c >= 127 || c < 32) {
      *q++ = '<';
      *q++ = "0123456789ABCDEF"[c >> 4];
      *q++ = "0123456789ABCDEF"[c & 15];
      *q++ = '>';
    } else {
      *q++ = c;
    }
  }
  *q = 0;
}

void format_utf16_be(void *from, u4 len, char *to)
{
  u2 *p = (u2 *)from;
  u2 *p_end;
  u1 *q = (u1 *)to;
  u2 c;

  if (len)
    p_end = (u2 *)(((u1 *)from) + len);
  else
    p_end = NULL;

  while (p_end == NULL || p < p_end) {
    c = get_be_short(p);
    if (c == 0)
      break;
    p++;  /* advance 2 bytes */

    if (c >= 127 || c < 32) {
      *q++ = '<';
      *q++ = "0123456789ABCDEF"[c >> 12];
      *q++ = "0123456789ABCDEF"[(c >> 8) & 15];
      *q++ = "0123456789ABCDEF"[(c >> 4) & 15];
      *q++ = "0123456789ABCDEF"[c & 15];
      *q++ = '>';
    } else {
      *q++ = (u1)c;
    }
  }
  *q = 0;
}

void format_utf16_le(void *from, u4 len, char *to)
{
  u2 *p = (u2 *)from;
  u2 *p_end;
  u1 *q = (u1 *)to;
  u2 c;

  if (len)
    p_end = (u2 *)(((u1 *)from) + len);
  else
    p_end = NULL;

  while (p_end == NULL || p < p_end) {
    c = get_le_short(p);
    if (c == 0)
      break;
    p++;  /* advance 2 bytes */

    if (c >= 127 || c < 32) {
      *q++ = '<';
      *q++ = "0123456789ABCDEF"[c >> 12];
      *q++ = "0123456789ABCDEF"[(c >> 8) & 15];
      *q++ = "0123456789ABCDEF"[(c >> 4) & 15];
      *q++ = "0123456789ABCDEF"[c & 15];
      *q++ = '>';
    } else {
      *q++ = (u1)c;
    }
  }
  *q = 0;
}

void format_uuid(void *uuid, char *to)
{
  u1 *from = uuid;
  int i, c, variant, version;

  if (memcmp(uuid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
    strcpy(to, "nil");
    return;
  }

  variant = from[8] >> 5;
  version = from[6] >> 4;

  for (i = 0; i < 16; i++) {
    c = *from++;
    *to++ = "0123456789ABCDEF"[c >> 4];
    *to++ = "0123456789ABCDEF"[c & 15];
    if (i == 3 || i == 5 || i == 7 || i == 9)
      *to++ = '-';
  }

  if ((variant & 4) == 0) {         /* 0 x x */
    strcpy(to, " (NCS)");
  } else if ((variant & 2) == 0) {  /* 1 0 x */
    sprintf(to, " (DCE, v%1.1d)", version);
  } else if ((variant & 1) == 0) {  /* 1 1 0 */
    strcpy(to, " (MS GUID)");
  } else {                          /* 1 1 1 */
    strcpy(to, " (Reserved)");
  }
}

void format_uuid_lvm(void *uuid, char *to)
{
  char *from = uuid;
  int i;

  for (i = 0; i < 32; i++) {
    *to++ = *from++;
    if ((i & 3) == 1 && i > 1 && i < 29)
      *to++ = '-';
  }
  *to = 0;
}

void format_guid(void *guid, char *to)
{
  u1 *from = guid;
  int i, c;

  if (memcmp(guid, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", 16) == 0) {
    strcpy(to, "nil");
    return;
  }

  for (i = 0; i < 16; i++) {
    c = *from++;
    *to++ = "0123456789ABCDEF"[c >> 4];
    *to++ = "0123456789ABCDEF"[c & 15];
    if (i == 3 || i == 5 || i == 7 || i == 9)
      *to++ = '-';
  }
  *to = 0;
}

/*
 * endian-aware data access
 */

u2 get_be_short(void *from)
{
  u1 *p = from;
  return ((u2)(p[0]) << 8) +
    (u2)p[1];
}

u4 get_be_long(void *from)
{
  u1 *p = from;
  return ((u4)(p[0]) << 24) +
    ((u4)(p[1]) << 16) +
    ((u4)(p[2]) << 8) +
    (u4)p[3];
}

u8 get_be_quad(void *from)
{
  u1 *p = from;
  return ((u8)(p[0]) << 56) +
    ((u8)(p[1]) << 48) +
    ((u8)(p[2]) << 40) +
    ((u8)(p[3]) << 32) +
    ((u8)(p[4]) << 24) +
    ((u8)(p[5]) << 16) +
    ((u8)(p[6]) << 8) +
    (u8)p[7];
}

u2 get_le_short(void *from)
{
  u1 *p = from;
  return ((u2)(p[1]) << 8) +
    (u2)p[0];
}

u4 get_le_long(void *from)
{
  u1 *p = from;
  return ((u4)(p[3]) << 24) +
    ((u4)(p[2]) << 16) +
    ((u4)(p[1]) << 8) +
    (u4)p[0];
}

u8 get_le_quad(void *from)
{
  u1 *p = from;
  return ((u8)(p[7]) << 56) +
    ((u8)(p[6]) << 48) +
    ((u8)(p[5]) << 40) +
    ((u8)(p[4]) << 32) +
    ((u8)(p[3]) << 24) +
    ((u8)(p[2]) << 16) +
    ((u8)(p[1]) << 8) +
    (u8)p[0];
}

u2 get_ve_short(int endianness, void *from)
{
  if (endianness)
    return get_le_short(from);
  else
    return get_be_short(from);
}

u4 get_ve_long(int endianness, void *from)
{
  if (endianness)
    return get_le_long(from);
  else
    return get_be_long(from);
}

u8 get_ve_quad(int endianness, void *from)
{
  if (endianness)
    return get_le_quad(from);
  else
    return get_be_quad(from);
}

const char * get_ve_name(int endianness)
{
  if (endianness)
    return "little-endian";
  else
    return "big-endian";
}

/*
 * more data access
 */

void get_string(void *from, int len, char *to)
{
  if (len > 255)
    len = 255;
  memcpy(to, from, len);
  to[len] = 0;
}

void get_pstring(void *from, char *to)
{
  int len = *(unsigned char *)from;
  memcpy(to, (char *)from + 1, len);
  to[len] = 0;
}

void get_padded_string(void *from, int len, char pad, char *to)
{
  int pos;

  get_string(from, len, to);

  for (pos = strlen(to) - 1; pos >= 0 && to[pos] == pad; pos--)
    to[pos] = 0;
}

int find_memory(void *haystack, int haystack_len,
                void *needle, int needle_len)
{
  int searchlen = haystack_len - needle_len + 1;
  int pos = 0;
  void *p;

  while (pos < searchlen) {
    p = memchr((char *)haystack + pos, *(unsigned char *)needle,
	       searchlen - pos);
    if (p == NULL)
      return -1;
    pos = (char *)p - (char *)haystack;
    if (memcmp(p, needle, needle_len) == 0)
      return pos;
    pos++;
  }

  return -1;
}

/*
 * error functions
 */

void error(const char *msg, ...)
{
  va_list par;
  char buf[4096];

  va_start(par, msg);
  vsnprintf(buf, 4096, msg, par);
  va_end(par);

  fprintf(stderr, PROGNAME ": %s\n", buf);
}

void errore(const char *msg, ...)
{
  va_list par;
  char buf[4096];

  va_start(par, msg);
  vsnprintf(buf, 4096, msg, par);
  va_end(par);

  fprintf(stderr, PROGNAME ": %s: %s\n", buf, strerror(errno));
}

void bailout(const char *msg, ...)
{
  va_list par;
  char buf[4096];

  va_start(par, msg);
  vsnprintf(buf, 4096, msg, par);
  va_end(par);

  fprintf(stderr, PROGNAME ": %s\n", buf);
  exit(1);
}

void bailoute(const char *msg, ...)
{
  va_list par;
  char buf[4096];

  va_start(par, msg);
  vsnprintf(buf, 4096, msg, par);
  va_end(par);

  fprintf(stderr, PROGNAME ": %s: %s\n", buf, strerror(errno));
  exit(1);
}

/* EOF */


syntax highlighted by Code2HTML, v. 0.9.1