#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <malloc.h>
#include <time.h>
#include <errno.h>

#define countof(array) (sizeof (array) / sizeof ((array)[0]))

#ifndef HAVE_STRLCPY
size_t
strlcpy (char *dst, const char *src, size_t size)
{
  const char *old = src;

  /* Copy as many bytes as will fit */
  if (size)
    {
      while (--size)
        {
          if (!(*dst++ = *src++))
            return src - old - 1;
        }
      
      *dst = 0;
    }

  while (*src++);
  return src - old -1;
}
#endif

char *
concat_strings_tim (const char *str0, ...)
{
  va_list args;
  const char *arg;
  size_t length = 0, pos = 0;
  char *s;

  if (!str0)
    return NULL;

  va_start(args, str0);
  for (arg = str0; arg; arg = va_arg (args, const char *))
    length += strlen (arg);
  va_end (args);

  s = malloc (length + 1);

  va_start (args, str0);
  for (arg = str0; arg; arg = va_arg (args, const char *))
    pos += strlcpy (s + pos, arg, length - pos + 1);
  va_end (args);

  return s;
}

char *
concat_strings_new (const char *str0, ...)
{
  va_list args;
  char *ret, *p;
  const char *next_str;
  size_t chunksize = 64;
  size_t bufsize = chunksize;
  size_t len;
  size_t total_length = 0;
  p = ret = malloc (bufsize);
  if (ret == NULL)
    {
      fprintf (stderr, "ERROR: Failed to allocate memory: %s\n",
	       strerror (errno));
      return NULL;
    }

  va_start (args, str0);
  for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
    {
      len = strlen (next_str);
      if (len == 0)
	    continue;
      total_length += len;
      if ( total_length > bufsize )
        {
          while (total_length > bufsize)
            {
              bufsize += chunksize;
              p = ret = realloc (ret, bufsize);
              if (ret == NULL)
                {
                  fprintf (stderr, "ERROR: Could not allocate memory: %s\n",
                       strerror (errno));
                  return NULL;
                }
            }
          p += total_length - len;
        }
      memcpy (p, next_str, len);
      p += len;
    }
  va_end (args);
  p = ret = realloc (ret, total_length + 1);
  p += total_length;
  *p = '\0';

  return ret;
}

/* Concatenate the NULL-terminated list of string arguments into
   freshly allocated space.  */

char *
concat_strings (const char *str0, ...)
{
  va_list args;
  int saved_lengths[5];		/* inspired by Apache's apr_pstrcat */
  char *ret, *p;

  const char *next_str;
  int total_length = 0;
  size_t argcount;

  /* Calculate the length of and allocate the resulting string. */

  argcount = 0;
  va_start (args, str0);
  for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
    {
      int len = strlen (next_str);
      if (argcount < countof (saved_lengths))
	saved_lengths[argcount++] = len;
      total_length += len;
    }
  va_end (args);
  p = ret = malloc (total_length + 1);

  /* Copy the strings into the allocated space. */

  argcount = 0;
  va_start (args, str0);
  for (next_str = str0; next_str != NULL; next_str = va_arg (args, char *))
    {
      int len;
      if (argcount < countof (saved_lengths))
	len = saved_lengths[argcount++];
      else
	len = strlen (next_str);
      memcpy (p, next_str, len);
      p += len;
    }
  va_end (args);
  *p = '\0';

  return ret;
}

int
main (int argc, char **argv)
{
  clock_t begin, end;
  double time_spent, avg_time;
  char *teststring;

  unsigned int iterations = 1000000;
  int i;

  
  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring =
	concat_strings ("This ", "is ", "a ", "string!\n", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings    : avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring =
	concat_strings_new ("This ", "is ", "a ", "string!\n", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings_new: avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring =
	concat_strings_tim ("This ", "is ", "a ", "string!\n", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings_tim: avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring = concat_strings ("What ", "you ", "say?", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings:     avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring = concat_strings_new ("What ", "you ", "say?", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings_new: avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring = concat_strings_tim ("What ", "you ", "say?", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings_tim: avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring =
	concat_strings ("This ", "is ", "a ", "very ", "long ", "string ",
			"with many arguments, ", "and ", "we're ", "trying ",
			"to ", "make sure that ", "nothing strange", " ",
			"happens.", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings:     avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring =
	concat_strings_new ("This ", "is ", "a ", "very ", "long ", "string ",
			"with many arguments, ", "and ", "we're ", "trying ",
			"to ", "make sure that ", "nothing strange", " ",
			"happens.", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings_new: avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  time_spent = 0;
  begin = clock ();
  for (i = 0; i < iterations; i++)
    {
      teststring =
	concat_strings_tim ("This ", "is ", "a ", "very ", "long ", "string ",
			    "with many arguments, ", "and ", "we're ",
			    "trying ", "to ", "make sure that ",
			    "nothing strange", " ", "happens.", (char *) 0);
      free (teststring);
    }
  end = clock ();
  time_spent = (double) (end - begin) / CLOCKS_PER_SEC;
  avg_time = time_spent / iterations;
  printf ("Result concat_strings_tim: avg %.8f s, total %.8f s\n", avg_time,
	  time_spent);

  return 0;
}
