/*
 * Raster error handling for CUPS.
 *
 * Copyright 2007-2015 by Apple Inc.
 * Copyright 2007 by Easy Software Products.
 *
 * These coded instructions, statements, and computer programs are the
 * property of Apple Inc. and are protected by Federal copyright
 * law.  Distribution and use rights are outlined in the file "LICENSE.txt"
 * which should have been included with this file.  If this file is
 * missing or damaged, see the license at "http://www.cups.org/".
 *
 * This file is subject to the Apple OS-Developed Software exception.
 */

/*
 * Include necessary headers...
 */

#include <cups/raster-private.h>


/*
 * Local structures...
 */

typedef struct _cups_raster_error_s	/**** Error buffer structure ****/
{
  char	*start,				/* Start of buffer */
	*current,			/* Current position in buffer */
	*end;				/* End of buffer */
} _cups_raster_error_t;


/*
 * Local functions...
 */

static _cups_raster_error_t	*get_error_buffer(void);


/*
 * '_cupsRasterAddError()' - Add an error message to the error buffer.
 */

void
_cupsRasterAddError(const char *f,	/* I - Printf-style error message */
                    ...)		/* I - Additional arguments as needed */
{
  _cups_raster_error_t	*buf = get_error_buffer();
					/* Error buffer */
  va_list	ap;			/* Pointer to additional arguments */
  char		s[2048];		/* Message string */
  ssize_t	bytes;			/* Bytes in message string */


  DEBUG_printf(("_cupsRasterAddError(f=\"%s\", ...)", f));

  va_start(ap, f);
  bytes = vsnprintf(s, sizeof(s), f, ap);
  va_end(ap);

  if (bytes <= 0)
    return;

  DEBUG_printf(("1_cupsRasterAddError: %s", s));

  bytes ++;

  if ((size_t)bytes >= sizeof(s))
    return;

  if (bytes > (ssize_t)(buf->end - buf->current))
  {
   /*
    * Allocate more memory...
    */

    char	*temp;			/* New buffer */
    size_t	size;			/* Size of buffer */


    size = (size_t)(buf->end - buf->start + 2 * bytes + 1024);

    if (buf->start)
      temp = realloc(buf->start, size);
    else
      temp = malloc(size);

    if (!temp)
      return;

   /*
    * Update pointers...
    */

    buf->end     = temp + size;
    buf->current = temp + (buf->current - buf->start);
    buf->start   = temp;
  }

 /*
  * Append the message to the end of the current string...
  */

  memcpy(buf->current, s, (size_t)bytes);
  buf->current += bytes - 1;
}


/*
 * '_cupsRasterClearError()' - Clear the error buffer.
 */

void
_cupsRasterClearError(void)
{
  _cups_raster_error_t	*buf = get_error_buffer();
					/* Error buffer */


  buf->current = buf->start;

  if (buf->start)
    *(buf->start) = '\0';
}


/*
 * 'cupsRasterErrorString()' - Return the last error from a raster function.
 *
 * If there are no recent errors, NULL is returned.
 *
 * @since CUPS 1.3/macOS 10.5@
 */

const char *				/* O - Last error */
cupsRasterErrorString(void)
{
  _cups_raster_error_t	*buf = get_error_buffer();
					/* Error buffer */


  if (buf->current == buf->start)
    return (NULL);
  else
    return (buf->start);
}


#ifdef HAVE_PTHREAD_H
/*
 * Implement per-thread globals...
 */

#  include <pthread.h>


/*
 * Local globals...
 */

static pthread_key_t	raster_key = 0;	/* Thread local storage key */
static pthread_once_t	raster_key_once = PTHREAD_ONCE_INIT;
					/* One-time initialization object */


/*
 * Local functions...
 */

static void	raster_init(void);
static void	raster_destructor(void *value);


/*
 * 'get_error_buffer()' - Return a pointer to thread local storage.
 */

_cups_raster_error_t *			/* O - Pointer to error buffer */
get_error_buffer(void)
{
  _cups_raster_error_t *buf;		/* Pointer to error buffer */


 /*
  * Initialize the global data exactly once...
  */

  DEBUG_puts("3get_error_buffer()");

  pthread_once(&raster_key_once, raster_init);

 /*
  * See if we have allocated the data yet...
  */

  if ((buf = (_cups_raster_error_t *)pthread_getspecific(raster_key))
          == NULL)
  {
    DEBUG_puts("4get_error_buffer: allocating memory for thread.");

   /*
    * No, allocate memory as set the pointer for the key...
    */

    buf = calloc(1, sizeof(_cups_raster_error_t));
    pthread_setspecific(raster_key, buf);

    DEBUG_printf(("4get_error_buffer: buf=%p", (void *)buf));
  }

 /*
  * Return the pointer to the data...
  */

  return (buf);
}


/*
 * 'raster_init()' - Initialize error buffer once.
 */

static void
raster_init(void)
{
  pthread_key_create(&raster_key, raster_destructor);

  DEBUG_printf(("3raster_init(): raster_key=%x(%u)", (unsigned)raster_key, (unsigned)raster_key));
}


/*
 * 'raster_destructor()' - Free memory allocated by get_error_buffer().
 */

static void
raster_destructor(void *value)		/* I - Data to free */
{
  _cups_raster_error_t *buf = (_cups_raster_error_t *)value;
					/* Error buffer */


  DEBUG_printf(("3raster_destructor(value=%p)", value));

  if (buf->start)
    free(buf->start);

  free(value);
}


#else
/*
 * Implement static globals...
 */

/*
 * 'get_error_buffer()' - Return a pointer to thread local storage.
 */

_cups_raster_error_t *			/* O - Pointer to error buffer */
get_error_buffer(void)
{
  static _cups_raster_error_t buf = { 0, 0, 0 };
					/* Error buffer */


  return (&buf);
}
#endif /* HAVE_PTHREAD_H */
