/*
 * Copyright (c) 2003-2016
 * Distributed Systems Software.  All rights reserved.
 * See the file BN-LICENSE for redistribution information.
 *
 * $Id: bn_print.c 2885 2016-05-16 23:48:04Z brachman $
 */

#include <stdlib.h>
#include <stdio.h>

#include "local.h"

static const char Hex[] = "0123456789ABCDEF";

/* Must 'OPENSSL_free' the returned data */
char *
BN_bn2hex(const BIGNUM *a)
{
  int i, j, v, z = 0;
  char *buf;
  char *p;

  if (a->neg && BN_is_zero(a)) {
	/* "-0" == 3 bytes including NULL terminator */
	buf = malloc(3);
  } else
	buf = malloc(a->top * BN_BYTES * 2 + 2);

  if (buf == NULL)
	goto err;
  p = buf;
  if (a->neg)
	*(p++) = '-';
  if (BN_is_zero(a))
	*(p++) = '0';
  for (i = a->top - 1; i >= 0; i--) {
	for (j = BN_BITS2 - 8; j >= 0; j -= 8) {
	  /* strip leading zeros */
	  v = ((int) (a->d[i] >> (long) j)) & 0xff;
	  if (z || (v != 0)) {
		*(p++) = Hex[v >> 4];
		*(p++) = Hex[v & 0x0f];
		z = 1;
	  }
	}
  }
  *p = '\0';
 err:
  return(buf);
}

/* Must 'OPENSSL_free' the returned data */
char *
BN_bn2dec(const BIGNUM *a)
{
  int i = 0, num, ok = 0;
  char *buf = NULL;
  char *p;
  BIGNUM *t = NULL;
  BN_ULONG *bn_data = NULL, *lp;

  /*-
   * get an upper bound for the length of the decimal integer
   * num <= (BN_num_bits(a) + 1) * log(2)
   *     <= 3 * BN_num_bits(a) * 0.1001 + log(2) + 1     (rounding error)
   *     <= BN_num_bits(a)/10 + BN_num_bits/1000 + 1 + 1
   */
  i = BN_num_bits(a) * 3;
  num = (i / 10 + i / 1000 + 1) + 1;
  bn_data = (BN_ULONG *) malloc((num / BN_DEC_NUM + 1) * sizeof(BN_ULONG));
  buf = (char *) malloc(num + 3);
  if ((buf == NULL) || (bn_data == NULL)) {
	goto err;
  }
  if ((t = BN_dup(a)) == NULL)
	goto err;

#define BUF_REMAIN (num+3 - (size_t)(p - buf))
  p = buf;
  lp = bn_data;
  if (BN_is_zero(t)) {
	*(p++) = '0';
	*(p++) = '\0';
  } else {
	if (BN_is_negative(t))
	  *p++ = '-';

	i = 0;
	while (!BN_is_zero(t)) {
	  *lp = BN_div_word(t, BN_DEC_CONV);
	  lp++;
	}
	lp--;
	/*
	 * We now have a series of blocks, BN_DEC_NUM chars in length, where
	 * the last one needs truncation. The blocks need to be reversed in
	 * order.
	 */
	snprintf(p, BUF_REMAIN, BN_DEC_FMT1, *lp);
	while (*p)
	  p++;
	while (lp != bn_data) {
	  lp--;
	  snprintf(p, BUF_REMAIN, BN_DEC_FMT2, *lp);
	  while (*p)
		p++;
	}
  }
  *p = '\0';
  ok = 1;

 err:
  if (bn_data != NULL)
	free(bn_data);
  if (t != NULL)
	BN_free(t);
  if (!ok && buf) {
	free(buf);
	buf = NULL;
  }

  return(buf);
}
