/* Test to exercise warnings when an array declared with attribute "nonstring"
   is passed to a function that expects a nul-terminated string as an argument.
   { dg-do compile }
   { dg-options "-O2 -Wattributes -Wstringop-overflow -ftrack-macro-expansion=0" }  */

typedef __SIZE_TYPE__       size_t;
typedef __builtin_va_list   va_list;

#if __cplusplus
extern "C" {
#endif

void* memchr (const void*, int, size_t);
int memcmp (const void*, const void*, size_t);
void* memcpy (void*, const void*, size_t);
void* memmove (void*, const void*, size_t);

int printf (const char*, ...);
int puts (const char*);
int puts_unlocked (const char*);
int sprintf (char*, const char*, ...);
int snprintf (char*, size_t, const char*, ...);
int vsprintf (char*, const char*, va_list);
int vsnprintf (char*, size_t, const char*, va_list);

int strcmp (const char*, const char*);
int strncmp (const char*, const char*, size_t);

char* stpcpy (char*, const char*);
char* stpncpy (char*, const char*, size_t);

char* strcat (char*, const char*);
char* strncat (char*, const char*, size_t);

char* strcpy (char*, const char*);
char* strncpy (char*, const char*, size_t);

char* strchr (const char*, int);
char* strdup (const char*);
size_t strlen (const char*);
size_t strnlen (const char*, size_t);
char* strndup (const char*, size_t);

#if __cplusplus
}   /* extern "C" */
#endif

#define NONSTRING __attribute__ ((nonstring))

/* STR needs to be bigger than ARR to trigger warnings, otherwise
   since STR must be a string, using both in a string function
   can be assumed to be safe even if ARR isn't nul-terminated.  */
char str[3][5];
char arr[3][4] NONSTRING;

char (*ptr)[6];
char (*parr)[6] NONSTRING;

struct MemArrays
{
  char str[5][5];
  char arr[4][4] NONSTRING;
  char (*parr)[4] NONSTRING;
};

void sink (int, ...);


#define T(call)  sink (0, call)

void test_printf (struct MemArrays *p)
{
  T (printf (str[2]));
  T (printf (arr[2]));             /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (printf (*ptr));
  T (printf (*parr));            /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (printf (p->str[2]));
  T (printf (p->arr[2]));          /* { dg-warning "argument 1 declared attribute .nonstring." } */
}


void test_puts (struct MemArrays *p)
{
  T (puts (str[2]));
  T (puts (arr[2]));               /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (puts (*ptr));
  T (puts (*parr));              /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (puts (p->str[2]));
  T (puts (p->arr[2]));            /* { dg-warning "argument 1 declared attribute .nonstring." } */
}


void test_snprintf (char *d, size_t n, struct MemArrays *p)
{
  T (snprintf (d, n, str[2]));
  T (snprintf (d, n, arr[2]));       /* { dg-warning "argument 3 declared attribute .nonstring." } */

  T (snprintf (d, n, *ptr));
  T (snprintf (d, n, *parr));      /* { dg-warning "argument 3 declared attribute .nonstring." } */

  T (snprintf (d, n, p->str[2]));
  T (snprintf (d, n, p->arr[2]));    /* { dg-warning "argument 3 declared attribute .nonstring." } */
}


void test_sprintf (char *d, struct MemArrays *p)
{
  T (sprintf (d, str[2]));
  T (sprintf (d, arr[2]));           /* { dg-warning "argument 2 declared attribute .nonstring." } */

  T (sprintf (d, *ptr));
  T (sprintf (d, *parr));          /* { dg-warning "argument 2 declared attribute .nonstring." } */

  T (sprintf (d, p->str[2]));
  T (sprintf (d, p->arr[2]));        /* { dg-warning "argument 2 declared attribute .nonstring." } */
}


void test_vsnprintf (char *d, size_t n, struct MemArrays *p, va_list va)
{
  T (vsnprintf (d, n, str[2], va));
  T (vsnprintf (d, n, arr[2], va));  /* { dg-warning "argument 3 declared attribute .nonstring." } */

  T (vsnprintf (d, n, *ptr, va));
  T (vsnprintf (d, n, *parr, va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */

  T (vsnprintf (d, n, p->str[2], va));
  T (vsnprintf (d, n, p->arr[2], va)); /* { dg-warning "argument 3 declared attribute .nonstring." } */
}


void test_vsprintf (char *d, struct MemArrays *p, va_list va)
{
  T (vsprintf (d, str[2], va));
  T (vsprintf (d, arr[2], va));      /* { dg-warning "argument 2 declared attribute .nonstring." } */

  T (vsprintf (d, *ptr, va));
  T (vsprintf (d, *parr, va));     /* { dg-warning "argument 2 declared attribute .nonstring." } */

  T (vsprintf (d, p->str[2], va));
  T (vsprintf (d, p->arr[2], va));   /* { dg-warning "argument 2 declared attribute .nonstring." } */
}


void test_strcmp (struct MemArrays *p)
{
  T (strcmp (str[2], str[2]));
  T (strcmp (str[2], arr[2]));          /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcmp (arr[2], str[2]));          /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strcmp (str[2], *ptr));
  T (strcmp (str[2], *parr));         /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcmp (*parr, str[2]));         /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strcmp (p->str[2], p->arr[2]));    /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcmp (p->arr[2], p->str[2]));    /* { dg-warning "argument 1 declared attribute .nonstring." } */
  T (strcmp (*p->parr, p->str[2]));   /* { dg-warning "argument 1 declared attribute .nonstring." } */
}


void test_strncmp_warn (struct MemArrays *p)
{
  enum { N = sizeof arr[2] };
  T (strncmp (str[2], arr[2], N));
  T (strncmp (arr[2], str[2], N));

  T (strncmp (str[2], arr[2], N + 1));   /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
  T (strncmp (arr[2], str[2], N + 1));   /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */

  T (strncmp (str[2], *parr, N + 1));
  T (strncmp (*parr, str[2], N + 1));

  T (strncmp (p->str[2], p->arr[2], N));
  T (strncmp (p->arr[2], p->str[2], N));
  T (strncmp (*p->parr, p->str[2], N));

  T (strncmp (p->str[2], p->arr[2], N));
  T (strncmp (p->arr[2], p->str[2], N));
  T (strncmp (*p->parr, p->str[2], N));
}


void test_strncmp_nowarn (struct MemArrays *p, size_t n)
{
  T (strncmp (str[2], str[2], n));
  T (strncmp (str[2], arr[2], n));
  T (strncmp (arr[2], str[2], n));

  T (strncmp (str[2], *ptr, n));
  T (strncmp (str[2], *parr, n));
  T (strncmp (*parr, str[2], n));

  T (strncmp (p->str[2], p->arr[2], n));
  T (strncmp (p->arr[2], p->str[2], n));
  T (strncmp (*p->parr, p->str[2], n));
}


void test_stpcpy (struct MemArrays *p)
{
  T (stpcpy (str[2], str[2]));
  T (stpcpy (str[2], arr[2]));          /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (stpcpy (arr[2], str[2]));

  T (stpcpy (str[2], *ptr));
  T (stpcpy (str[2], *parr));         /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (stpcpy (*parr, str[2]));

  T (stpcpy (p->str[2], p->arr[2]));    /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (stpcpy (p->arr[2], p->str[2]));
  T (stpcpy (*p->parr, p->str[2]));
}


void test_stpncpy_nowarn (struct MemArrays *p, unsigned n)
{
  T (stpncpy (str[2], str[2], n));
  T (stpncpy (str[2], arr[2], n));
  T (stpncpy (arr[2], str[2], n));

  T (stpncpy (str[2], *ptr, n));
  T (stpncpy (str[2], *parr, n));
  T (stpncpy (*parr, str[2], n));

  T (stpncpy (p->str[2], p->arr[2], n));
  T (stpncpy (p->arr[2], p->str[2], n));
  T (stpncpy (*p->parr, p->str[2], n));
}


void test_stpncpy_warn (struct MemArrays *p, unsigned n)
{
  enum { N = sizeof arr[2] };

  T (stpncpy (str[2], str[2], N));
  T (stpncpy (str[2], arr[2], N));
  T (stpncpy (arr[2], str[2], N));

  T (stpncpy (str[2], *ptr, N));
  T (stpncpy (str[2], *parr, N));
  T (stpncpy (*parr, str[2], N));

  T (stpncpy (p->str[2], p->arr[2], N));
  T (stpncpy (p->arr[2], p->str[2], N));
  T (stpncpy (*p->parr, p->str[2], N));

  T (stpncpy (*ptr, str[2], N + 1));
  T (stpncpy (*ptr, arr[2], N + 1));          /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
  T (stpncpy (arr[2], str[2], N + 1));          /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */

  T (stpncpy (*ptr, *ptr, N + 1));
  T (stpncpy (*ptr, *parr, N + 1));
  T (stpncpy (*parr, str[2], N + 1));

  T (stpncpy (*ptr, p->arr[2], N + 1));       /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */
  T (stpncpy (p->arr[2], p->str[2], N + 1));    /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */
  T (stpncpy (*p->parr, p->str[2], N + 1));
}


void test_strcat (struct MemArrays *p)
{
  T (strcat (str[2], str[2]));
  T (strcat (str[2], arr[2]));          /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcat (arr[2], str[2]));

  T (strcat (str[2], *ptr));
  T (strcat (str[2], *parr));         /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcat (*parr, str[2]));

  T (strcat (p->str[2], p->arr[2]));    /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcat (p->arr[2], p->str[2]));
  T (strcat (*p->parr, p->str[2]));
}


void test_strncat (struct MemArrays *p, unsigned n)
{
  T (strncat (str[2], str[2], n));
  T (strncat (str[2], arr[2], n));      /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strncat (arr[2], str[2], n));

  T (strncat (str[2], *ptr, n));
  T (strncat (str[2], *parr, n));     /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strncat (*parr, str[2], n));

  T (strncat (p->str[2], p->arr[2], n));   /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strncat (p->arr[2], p->str[2], n));
  T (strncat (*p->parr, p->str[2], n));
}


void test_strcpy (struct MemArrays *p)
{
  T (strcpy (str[2], str[2]));
  T (strcpy (str[2], arr[2]));          /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcpy (arr[2], str[2]));

  T (strcpy (str[2], *ptr));
  T (strcpy (str[2], *parr));         /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcpy (*parr, str[2]));

  T (strcpy (p->str[2], p->arr[2]));    /* { dg-warning "argument 2 declared attribute .nonstring." } */
  T (strcpy (p->arr[2], p->str[2]));
  T (strcpy (*p->parr, p->str[2]));
}


void test_strncpy (struct MemArrays *p, unsigned n)
{
  T (strncpy (str[2], str[2], n));
  T (strncpy (str[2], arr[2], n));
  T (strncpy (arr[2], str[2], n));

  T (strncpy (str[2], *ptr, n));
  T (strncpy (str[2], *parr, n));
  T (strncpy (*parr, str[2], n));

  T (strncpy (p->str[2], p->arr[2], n));
  T (strncpy (p->arr[2], p->str[2], n));
  T (strncpy (*p->parr, p->str[2], n));
}


void test_strchr (struct MemArrays *p, int c)
{
  T (strchr (str[2], c));
  T (strchr (arr[2], c));          /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strchr (*ptr, c));
  T (strchr (*parr, c));         /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strchr (p->str[2], c));
  T (strchr (p->arr[2], c));       /* { dg-warning "argument 1 declared attribute .nonstring." } */
}


void test_strdup (struct MemArrays *p)
{
  T (strdup (str[2]));
  T (strdup (arr[2]));             /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strdup (*ptr));
  T (strdup (*parr));            /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strdup (p->str[2]));
  T (strdup (p->arr[2]));          /* { dg-warning "argument 1 declared attribute .nonstring." } */
}


void test_stnrdup_nowarn (struct MemArrays *p, size_t n)
{
  T (strndup (str[2], n));
  T (strndup (arr[2], n));

  T (strndup (*ptr, n));
  T (strndup (*parr, n));

  T (strndup (p->str[2], n));
  T (strndup (p->arr[2], n));
}


void test_stnrdup_warn (struct MemArrays *p)
{
  enum { N = sizeof arr[2] };

  T (strndup (str[2], N));
  T (strndup (arr[2], N));

  T (strndup (*ptr, N));
  T (strndup (*parr, N));

  T (strndup (p->str[2], N));
  T (strndup (p->arr[2], N));


  T (strndup (arr[2], N + 1));     /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */
  T (strndup (*parr, N + 1));
  T (strndup (p->arr[2], N + 1));  /* { dg-warning "argument 1 declared attribute 'nonstring' is smaller than the specified bound 5|specified bound 5 exceeds source size 4" } */
  T (strndup (*p->parr, N + 1));
}


void test_strlen (struct MemArrays *p, char *s NONSTRING, size_t n)
{
  T (strlen (str[2]));
  T (strlen (arr[2]));             /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strlen (*ptr));
  T (strlen (*parr));            /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strlen (p->str[2]));
  T (strlen (p->arr[2]));          /* { dg-warning "argument 1 declared attribute .nonstring." } */

  T (strlen (s));               /* { dg-warning "argument 1 declared attribute .nonstring." } */
  {
    strcpy (s, "123");
    T (strlen (s));
  }

  {
    char a[][3] __attribute__ ((nonstring)) = { { 1, 2, 3 } };

    T (strlen (a[0]));             /* { dg-warning "argument 1 declared attribute .nonstring." } */
  }

  {
    char a[][4] __attribute__ ((nonstring)) = { { 1, 2, 3, 4 } };

    strcpy (a[0], "12");
    T (strlen (a[0]));
  }
}


void test_strnlen (struct MemArrays *p, size_t n)
{
  T (strnlen (str[2], n));
  T (strnlen (arr[2], n));

  T (strnlen (*ptr, n));
  T (strnlen (*parr, n));

  T (strnlen (p->str[2], n));
  T (strnlen (p->arr[2], n));
}


/* Verify no warnings are issued for raw mempory functions.  */

void test_mem_functions (struct MemArrays *p, int c, size_t n)
{
  T (memchr (arr[2], c, n));
  T (memchr (*parr, c, n));
  T (memchr (p->arr[2], c, n));
  T (memchr (*p->parr, c, n));

  T (memcmp (str[2], arr[2], n));
  T (memcmp (arr[2], str[2], n));
  T (memcmp (str[2], *parr, n));
  T (memcmp (*parr, str[2], n));
  T (memcmp (p->str[2], p->arr[2], n));
  T (memcmp (p->arr[2], p->str[2], n));
  T (memcmp (*p->parr, p->str[2], n));

  T (memcpy (str[2], arr[2], n));
  T (memcpy (arr[2], str[2], n));
  T (memcpy (str[2], *parr, n));
  T (memcpy (*parr, str[2], n));
  T (memcpy (p->str[2], p->arr[2], n));
  T (memcpy (p->arr[2], p->str[2], n));
  T (memcpy (*p->parr, p->str[2], n));

  T (memmove (str[2], arr[2], n));
  T (memmove (arr[2], str[2], n));
  T (memmove (str[2], *parr, n));
  T (memmove (*parr, str[2], n));
  T (memmove (p->str[2], p->arr[2], n));
  T (memmove (p->arr[2], p->str[2], n));
  T (memmove (*p->parr, p->str[2], n));
}