/* You don't really want to know what this hack is for.
   Copyright (C) 1996-2013 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <http://www.gnu.org/licenses/>.  */

#include <assert.h>
#include <ctype.h>
#include <dlfcn.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static void *funcall (char **stringp) __attribute_noinline__;
static void *eval (char **stringp);


long int weak_function
__strtol_internal (const char *nptr, char **endptr, int base, int group)
{
  unsigned long int result = 0;
  long int sign = 1;

  while (*nptr == ' ' || *nptr == '\t')
    ++nptr;

  if (*nptr == '-')
    {
      sign = -1;
      ++nptr;
    }
  else if (*nptr == '+')
    ++nptr;

  if (*nptr < '0' || *nptr > '9')
    {
      if (endptr != NULL)
	*endptr = (char *) nptr;
      return 0L;
    }

  assert (base == 0);
  base = 10;
  if (*nptr == '0')
    {
      if (nptr[1] == 'x' || nptr[1] == 'X')
	{
	  base = 16;
	  nptr += 2;
	}
      else
	base = 8;
    }

  while (*nptr >= '0' && *nptr <= '9')
    {
      unsigned long int digval = *nptr - '0';
      if (result > LONG_MAX / 10
	  || (sign > 0 ? result == LONG_MAX / 10 && digval > LONG_MAX % 10
	      : (result == ((unsigned long int) LONG_MAX + 1) / 10
		 && digval > ((unsigned long int) LONG_MAX + 1) % 10)))
	{
	  errno = ERANGE;
	  return sign > 0 ? LONG_MAX : LONG_MIN;
	}
      result *= base;
      result += digval;
      ++nptr;
    }

  return (long int) result * sign;
}


static void *
funcall (char **stringp)
{
  void *args[strlen (*stringp)], **ap = args;
  void *argcookie = &args[1];

  do
    {
      /* Evaluate the next token.  */
      *ap++ = eval (stringp);

      /* Whitespace is irrelevant.  */
      while (isspace (**stringp))
	++*stringp;

      /* Terminate at closing paren or end of line.  */
    } while (**stringp != '\0' && **stringp != ')');
  if (**stringp != '\0')
    /* Swallow closing paren.  */
    ++*stringp;

  if (args[0] == NULL)
    {
      static const char unknown[] = "Unknown function\n";
      write (1, unknown, sizeof unknown - 1);
      return NULL;
    }

  /* Do it to it.  */
  __builtin_return (__builtin_apply (args[0],
				     &argcookie,
				     (char *) ap - (char *) &args[1]));
}

static void *
eval (char **stringp)
{
  void *value;
  char *p = *stringp, c;

  /* Whitespace is irrelevant.  */
  while (isspace (*p))
    ++p;

  switch (*p)
    {
    case '"':
      /* String constant.  */
      value = ++p;
      do
	if (*p == '\\')
	  {
	    switch (*strcpy (p, p + 1))
	      {
	      case 't':
		*p = '\t';
		break;
	      case 'n':
		*p = '\n';
		break;
	      }
	    ++p;
	  }
      while (*p != '\0' && *p++ != '"');
      if (p[-1] == '"')
	p[-1] = '\0';
      break;

    case '(':
      *stringp = ++p;
      return funcall (stringp);

    default:
      /* Try to parse it as a number.  */
      value = (void *) __strtol_internal (p, stringp, 0, 0);
      if (*stringp != p)
	return value;

      /* Anything else is a symbol that produces its address.  */
      value = p;
      do
	++p;
      while (*p != '\0' && !isspace (*p) && (!ispunct (*p) || *p == '_'));
      c = *p;
      *p = '\0';
      value = dlsym (NULL, value);
      *p = c;
      break;
    }

  *stringp = p;
  return value;
}


extern void _start (void) __attribute__ ((noreturn));
void
__attribute__ ((noreturn))
_start (void)
{
  char *buf = NULL;
  size_t bufsz = 0;

  while (__getdelim (&buf, &bufsz, '\n', stdin) > 0)
    {
      char *p = buf;
      eval (&p);
    }

  exit (0);
}
