/* vim: set ts=2 et sw=2 : */
/** @file errors.c */
/*
 *  T50 - Experimental Mixed Packet Injector
 *
 *  Copyright (C) 2010 - 2025 - T50 developers
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// Needed for asprintf() -- see below.
#define _GNU_SOURCE

#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <configuration.h>
#include <t50_defines.h>

// Some implementations of glibc lacks asprintf family of functions...
// Here I'm changing the function to return the allocated pointer or NULL,
// in case of failure.
//
// Define DEFINE_ASPRINTF symbol if your glibc don't have asprintf function.
//
#ifdef DEFINE_ASPRINTF

// These are the portable vasprintf and asprintf modified to
// return a pointer...
static char *vasprintf_( const char * const fmt, va_list args )
{
  int n;
  va_list copy;
  char *p = NULL;

  va_copy( copy, args );

    // snprintf() and vsnprintf() returns the number of chars that would be
    // printed, if not limited by 'size' (0 size means these functions will
    // not touch the pointer, which can be NULL).
    n = vsnprintf( NULL, 0, fmt, args );

  va_end( copy );

  if ( n >= 0 )
  {
    p = malloc( ++n );
    if ( p )
      vsprintf( p, fmt, args );
  }

  return p;
}

static char *asprintf_( const char * const fmt, ... )
{
  char *p;
  va_list args;

  va_start( args, fmt );
  p = vasprintf_( fmt, args );
  va_end( args );

  return p;
}
#else
static char *asprintf_( const char * const fmt, ... )
{
  char *p;
  va_list args;

  va_start( args, fmt );

  // asprintf(), until C23, is ambigous about setting `p` to NULL
  // if failure.
  if ( asprintf( &p, fmt, args ) < 0 )
    p = NULL;

  va_end( args );

  return p;
}
#endif

/* --- Using vfprintf for flexibility. */
static void verror ( const char *const fmt, va_list args )
{
  char *str;

  str = asprintf_( PACKAGE ": %s\n", fmt );

  if ( ! str )
  {
    fputs ( PACKAGE ": Unknown error (not enough memory?).\n", stderr );
    exit ( EXIT_FAILURE );
  }

  vfprintf ( stderr, str, args );

  free ( str );
}

/**
 * Standard error reporting routine. Non fatal version.
 */
void error ( const char *const fmt, ... )
{
  va_list args;

  fputs ( ERROR " ", stderr );

  va_start ( args, fmt );
  verror ( fmt, args );
  va_end ( args );
}

/**
 * Standard error reporting routine. Fatal Version.
 *
 * This function never returns!
 */
void fatal_error ( const char *const fmt, ... )
{
  va_list args;

  fputs ( FATAL " ", stderr );

  va_start ( args, fmt );
  verror ( fmt, args );
  va_end ( args );

  /* As expected. exit if a failure. */
  exit ( EXIT_FAILURE );
}
