/* chksnprintf.c */

/* Copyright 1998, 1999 by Eberhard Mattes <mattes@em-gw@windhager.de>
   Donated to the public domain.  No warranty.

   1998-09-03 Initial version
   1999-01-01 #include "firewall.h" */

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "firewall.h"		/* DONT_HAVE_VSNPRINTF */
#include "libemfw.h"

/* Call check_snprintf() to check if snprintf() and vsnprintf() really
   restrict the output to the limit passed as argument.  Rather than
   creating a separate program for checking snprintf() and
   vsnprintf(), this function should be called from the application,
   to make sure that linking options and libraries (libdb on Linux!)
   don't affect the result. */

static int warning;

#ifndef DONT_HAVE_VSNPRINTF
static char buf[1024];

static void fix (void)
{
  puts ("Please add the line\n");
  puts ("  #define DONT_HAVE_VSNPRINTF\n");
  puts ("to firewall.h, run \"make clean\" in all relevant directories,");
  puts ("and rebuild.");
  exit (2);
}

static void fail (int n)
{
  printf ("\nYour snprintf() and/or vsnprintf() functions are broken, "
	  "case %d.\n\n", n);
  fix ();
}

void test1 (void)
{
  int n;

  memset (buf, 'x', sizeof (buf));
  n = snprintf (buf, 5, "%.23s", "abcdefghijklmnopqrstuvwxyz");
  if (n == -1)
    warning = 1;
  else if (n != 23)
    fail (1);
  if (memcmp (buf, "abcd\000xxxx", 9) != 0)
    fail (2);

  memset (buf, 'x', sizeof (buf));
  n = snprintf (buf, 2, "%ld", 98765432L);
  if (n == -1)
    warning = 1;
  else if (n != 8)
    fail (3);
  if (memcmp (buf, "9\000xxxx", 6) != 0)
    fail (4);
}

void test2 (size_t limit, int nexp, const char *sexp, size_t sexplen,
	    int casenum, const char *fmt, ...)
{
  int n;
  va_list arg_ptr;

  memset (buf, 'x', sizeof (buf));
  va_start (arg_ptr, fmt);
  n = vsnprintf (buf, limit, fmt, arg_ptr);
  if (n == -1)
    warning = 1;
  else if (n != nexp)
    fail (casenum+0);
  if (memcmp (buf, sexp, sexplen) != 0)
    fail (casenum+1);
}
#endif

void check_snprintf (int level)
{
#ifndef DONT_HAVE_VSNPRINTF
  test1 ();
  test2 (5, 23, "abcd\000xxxx", 9, 5, "%.23s", "abcdefghijklmnopqrstuvwxyz");
  test2 (2, 8, "9\000xxxx", 6, 7, "%ld", 98765432L);
  if (warning)
    {
      puts ("\nThe return value of this system's snprintf() and/or "
	    "vsnprintf() functions");
      fputs ("is incorrect", stdout);
      if (level >= 1)
	{
	  puts (".\n");
	  fix ();
	}
      else
	puts (", but that doesn't preclude their usage in this program.\n");
    }
#else
  warning = level;		/* Keep the compiler happy */
#endif
}
