/* Copyright (c) 1993-2008 by Richard Kelsey and Jonathan Rees.
   See file COPYING. */

/*
 * Generation of random strings for unique syms.
 */

/*
 * Unix-only at this point.  Windows should utilize CryptGenRandom.
 */

#include "sysdep.h"
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "scheme48.h"


/*
 * Does not write terminating NUL.
 */

void
s48_randomize_string(s48_value sch_str)
{
  static const char* valid_chars = 
    "$%&/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; /* valid symbol characters */
  static size_t valid_chars_count = 0;
  size_t size = S48_STRING_LENGTH(sch_str);
  if (!valid_chars_count)
    valid_chars_count = strlen(valid_chars);

#ifdef HAVE_URANDOM
  static int fd = -1;
  char* buf = S48_ADDRESS_AFTER_HEADER(sch_str, char); /* hack to avoid allocation */
  if (fd == -1)
    {
      for (;;)
	{
	  fd = open("/dev/urandom", O_RDONLY);
	  if (fd >= 0)
	    break;
	  if (errno != EINTR)
	    {
	      fprintf(stderr, "unable to open /dev/urandom");
	      exit(-1);
	    }
	}
    }
  for (;;)
    {
      ssize_t status = read(fd, buf, size);
      if (status == size)
	break;
      else if ((status == -1) && (errno == EINTR))
	continue;
      else
	{
	  fprintf(stderr, "unable to read data from /dev/urandom");
	  exit(-1);
	}
    }
  {
    size_t i = size;
    while (i > 0)
      {
	S48_STRING_SET(sch_str, i-1,
		       valid_chars[((unsigned)buf[i-1]) % valid_chars_count]);
	--i;
      }
  }
#else
  /*
   * bad, bad, bad
   */
  {
      static int initialized = 0;
      if (!initialized)
	{
	  struct timeval tv;
	  gettimeofday(&tv, NULL);
	  srand((unsigned) tv.tv_usec);
	  initialized = 1;
	}
      {
	size_t i = 0;
	while (i < size)
	  {
	    S48_STRING_SET(sch_str, i, valid_chars[((unsigned) rand()) % valid_chars_count]);
	    ++i;
	  }
      }
  }
#endif
}
