/* call1.c (emx+gcc) */

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

/* Regression test for calling conventions. */

#ifdef ATTR1
#define CALL1 __attribute__((ATTR1))
#else
#define CALL1
#endif

#ifdef ATTR2
#define CALL2 __attribute__((ATTR2))
#else
#define CALL2
#endif

struct small
{
  int x, y;
};

#define NBIG    32

struct big
{
  int a[NBIG];
};

static int skip_2g;

static struct small CALL1 f_a (int x, int y)
{
  struct small r;

  r.x = -x;
  r.y = -y;
  return r;
}


static struct big CALL1 f_b (int x)
{
  struct big r;
  int i;

  for (i = 0; i < NBIG; ++i)
    r.a[i] = -(x+i);
  return r;
}


static void CALL1 f_c (int x, struct big y)
{
  int i;

  for (i = 0; i < NBIG; ++i)
    if (y.a[i] != -(x+i))
      abort ();
}

static int CALL1 f_d (int n, ...)
{
  int i, x;
  va_list ap;

  if (n != 7) abort ();
  va_start (ap, n);
  for (i = 0; i < n; ++i)
    {
      x = va_arg (ap, int);
      if (x != i) abort ();
    }
  va_end (ap);
  return 17;
}

static struct small CALL1 f_e (int n, ...)
{
  int i, x;
  struct small r;
  va_list ap;

  if (n != 7) abort ();
  va_start (ap, n);
  for (i = 0; i < n; ++i)
    {
      x = va_arg (ap, int);
      if (x != i + 1) abort ();
    }
  va_end (ap);

  r.x = 42;
  r.y = -17;
  return r;
}

static struct big CALL1 f_f (int n, ...)
{
  int i, x;
  struct big r;
  va_list ap;

  if (n != 5) abort ();
  va_start (ap, n);
  for (i = 0; i < n; ++i)
    {
      x = va_arg (ap, int);
      if (x != i + 2) abort ();
    }
  va_end (ap);

  for (i = 0; i < NBIG; ++i)
    r.a[i] = -(n+i);
  return r;
}

static void CALL1 f_g (int a, int b, int c, int d, int e)
{
  if (a != 17 || b != 18 || c != 19 || d != 20 || e != 21)
    abort ();
}

static void CALL2 test1 (void)
{
  struct small s;
  struct big b;
  static char *p1;
  int i;

  p1 = alloca (0);
  s = f_a (1, 3);
  if (p1 != alloca (0))
    abort ();
  if (s.x != -1 || s.y != -3)
    abort ();
  b = f_b (1);
  if (p1 != alloca (0))
    abort ();
  f_c (1, b);
  if (p1 != alloca (0))
    abort ();
  i = f_d (7, 0, 1, 2, 3, 4, 5, 6, 7);
  if (i != 17) abort ();
  if (p1 != alloca (0))
    abort ();
  s = f_e (7, 1, 2, 3, 4, 5, 6, 7, 8);
  if (p1 != alloca (0))
    abort ();
  if (s.x != 42 || s.y != -17)
    abort ();
  b = f_f (5, 2, 3, 4, 5, 6);
  if (p1 != alloca (0))
    abort ();
  f_c (5, b);
  if (p1 != alloca (0))
    abort ();
  f_g (17, 18, 19, 20, 21);
  if (p1 != alloca (0))
    abort ();
}

static void CALL2 test2 (struct small (*ca)(int, int) CALL1,
                         struct big (*cb)(int) CALL1,
                         void (*cc)(int, struct big) CALL1,
                         int (*cd)(int, ...) CALL1,
                         struct small (*ce)(int, ...) CALL1,
                         struct big (*cf)(int, ...) CALL1,
                         void (*cg)(int, int, int, int, int) CALL1)
{
  struct small s;
  struct big b;
  static char *p1;
  int i;

  p1 = alloca (0);
  s = ca (1, 3);
  if (p1 != alloca (0))
    abort ();
  if (s.x != -1 || s.y != -3)
    abort ();
  b = cb (1);
  if (p1 != alloca (0))
    abort ();
  cc (1, b);
  if (p1 != alloca (0))
    abort ();
  i = cd (7, 0, 1, 2, 3, 4, 5, 6, 7);
  if (i != 17) abort ();
  if (p1 != alloca (0))
    abort ();
  s = ce (7, 1, 2, 3, 4, 5, 6, 7, 8);
  if (p1 != alloca (0))
    abort ();
  if (s.x != 42 || s.y != -17)
    abort ();
  b = cf (5, 2, 3, 4, 5, 6);
  if (p1 != alloca (0))
    abort ();
  cc (5, b);
  if (p1 != alloca (0))
    abort ();
  if (!skip_2g)
    cg (17, 18, 19, 20, 21);
  if (p1 != alloca (0))
    abort ();
}


int __attribute__ ((__emxcall__)) main (int argc, char *argv[])
{
  static char *p1;

  if (argc == 2 && strcmp (argv[1], "-2g") == 0)
    skip_2g = 1;                /* GCC bug reported */
  else if (argc != 1)
    {
      puts ("Usage: call1 [-2g]");
      exit (1);
    }

  p1 = alloca (0);
  test1 ();
  if (p1 != alloca (0)) abort ();
  test2 (f_a, f_b, f_c, f_d, f_e, f_f, f_g);
  if (p1 != alloca (0)) abort ();
  return 0;
}
