/*
 * Status buffer routines for the CUPS scheduler.
 *
 * Copyright © 2020-2026 by OpenPrinting.
 * Copyright © 2007-2014 by Apple Inc.
 * Copyright © 1997-2006 by Easy Software Products, all rights reserved.
 *
 * Licensed under Apache License v2.0.  See the file "LICENSE" for more
 * information.
 */

/*
 * Include necessary headers...
 */

#include "cupsd.h"
#include <stdarg.h>


/*
 * 'cupsdStatBufDelete()' - Destroy a status buffer.
 */

void
cupsdStatBufDelete(cupsd_statbuf_t *sb)	/* I - Status buffer */
{
 /*
  * Range check input...
  */

  if (!sb)
    return;

 /*
  * Close the status pipe and free memory used...
  */

  close(sb->fd);

  free(sb);
}


/*
 * 'cupsdStatBufNew()' - Create a new status buffer.
 */

cupsd_statbuf_t	*			/* O - New status buffer */
cupsdStatBufNew(int        fd,		/* I - File descriptor of pipe */
                const char *prefix,	/* I - Printf-style prefix string */
		...)			/* I - Additional args as needed */
{
  cupsd_statbuf_t	*sb;		/* New status buffer */
  va_list		ap;		/* Argument list */


 /*
  * Range check input...
  */

  if (fd < 0)
    return (NULL);

 /*
  * Allocate the status buffer...
  */

  if ((sb = calloc(1, sizeof(cupsd_statbuf_t))) != NULL)
  {
   /*
    * Assign the file descriptor...
    */

    sb->fd = fd;

   /*
    * Format the prefix string, if any.  This is usually "[Job 123]"
    * or "[Sub 123]", and so forth.
    */

    if (prefix)
    {
     /*
      * Printf-style prefix string...
      */

      va_start(ap, prefix);
      vsnprintf(sb->prefix, sizeof(sb->prefix), prefix, ap);
      va_end(ap);
    }
    else
    {
     /*
      * No prefix string...
      */

      sb->prefix[0] = '\0';
    }
  }

  return (sb);
}


/*
 * 'cupsdStatBufUpdate()' - Update the status buffer.
 */

char *					/* O - Line from buffer, "", or NULL */
cupsdStatBufUpdate(
    cupsd_statbuf_t *sb,		/* I - Status buffer */
    int             *loglevel,		/* O - Log level */
    char            *line,		/* I - Line buffer */
    int             linelen)		/* I - Size of line buffer */
{
  int		bytes;			/* Number of bytes read */
  char		*lineptr,		/* Pointer to end of line in buffer */
		*message;		/* Pointer to message text */


 /*
  * Check if the buffer already contains a full line...
  */

  if ((lineptr = strchr(sb->buffer, '\n')) == NULL)
  {
   /*
    * No, read more data...
    */

    if ((bytes = CUPSD_SB_BUFFER_SIZE - 1 - sb->bufused) > 0)
      bytes = read(sb->fd, sb->buffer + sb->bufused, (size_t)bytes);

    if (bytes > 0)
    {
      sb->bufused += bytes;
      sb->buffer[sb->bufused] = '\0';

     /*
      * Guard against a line longer than the max buffer size...
      */

      if ((lineptr = strchr(sb->buffer, '\n')) == NULL && sb->bufused == (CUPSD_SB_BUFFER_SIZE - 1))
	lineptr = sb->buffer + sb->bufused;
    }
    else if (bytes < 0 && errno == EINTR)
    {
     /*
      * Return an empty line if we are interrupted...
      */

      *loglevel = CUPSD_LOG_NONE;
      line[0]   = '\0';

      return (line);
    }
    else
    {
     /*
      * End-of-file, so use the whole buffer...
      */

      lineptr = sb->buffer + sb->bufused;
    }

   /*
    * Final check for end-of-file...
    */

    if (sb->bufused == 0 && bytes == 0)
      lineptr = NULL;
  }

  if (!lineptr)
  {
   /*
    * End of file...
    */

    *loglevel = CUPSD_LOG_NONE;
    line[0]   = '\0';

    return (NULL);
  }

 /*
  * Terminate the line and process it...
  */

  if (*lineptr)
    *lineptr++ = '\0';

 /*
  * Figure out the logging level...
  */

  if (!strncmp(sb->buffer, "EMERG:", 6))
  {
    *loglevel = CUPSD_LOG_EMERG;
    message   = sb->buffer + 6;
  }
  else if (!strncmp(sb->buffer, "ALERT:", 6))
  {
    *loglevel = CUPSD_LOG_ALERT;
    message   = sb->buffer + 6;
  }
  else if (!strncmp(sb->buffer, "CRIT:", 5))
  {
    *loglevel = CUPSD_LOG_CRIT;
    message   = sb->buffer + 5;
  }
  else if (!strncmp(sb->buffer, "ERROR:", 6))
  {
    *loglevel = CUPSD_LOG_ERROR;
    message   = sb->buffer + 6;
  }
  else if (!strncmp(sb->buffer, "WARNING:", 8))
  {
    *loglevel = CUPSD_LOG_WARN;
    message   = sb->buffer + 8;
  }
  else if (!strncmp(sb->buffer, "NOTICE:", 7))
  {
    *loglevel = CUPSD_LOG_NOTICE;
    message   = sb->buffer + 7;
  }
  else if (!strncmp(sb->buffer, "INFO:", 5))
  {
    *loglevel = CUPSD_LOG_INFO;
    message   = sb->buffer + 5;
  }
  else if (!strncmp(sb->buffer, "DEBUG:", 6))
  {
    *loglevel = CUPSD_LOG_DEBUG;
    message   = sb->buffer + 6;
  }
  else if (!strncmp(sb->buffer, "DEBUG2:", 7))
  {
    *loglevel = CUPSD_LOG_DEBUG2;
    message   = sb->buffer + 7;
  }
  else if (!strncmp(sb->buffer, "PAGE:", 5))
  {
    *loglevel = CUPSD_LOG_PAGE;
    message   = sb->buffer + 5;
  }
  else if (!strncmp(sb->buffer, "STATE:", 6))
  {
    *loglevel = CUPSD_LOG_STATE;
    message   = sb->buffer + 6;
  }
  else if (!strncmp(sb->buffer, "JOBSTATE:", 9))
  {
    *loglevel = CUPSD_LOG_JOBSTATE;
    message   = sb->buffer + 9;
  }
  else if (!strncmp(sb->buffer, "ATTR:", 5))
  {
    *loglevel = CUPSD_LOG_ATTR;
    message   = sb->buffer + 5;
  }
  else if (!strncmp(sb->buffer, "PPD:", 4))
  {
    *loglevel = CUPSD_LOG_PPD;
    message   = sb->buffer + 4;
  }
  else
  {
    *loglevel = CUPSD_LOG_DEBUG;
    message   = sb->buffer;
  }

 /*
  * Skip leading whitespace in the message...
  */

  while (isspace(*message & 255))
    message ++;

 /*
  * Send it to the log file as needed...
  */

  if (sb->prefix[0])
  {
    if (*loglevel > CUPSD_LOG_NONE && (*loglevel != CUPSD_LOG_INFO || LogLevel >= CUPSD_LOG_DEBUG))
    {
     /*
      * General status message; send it to the error_log file...
      */

      if (message[0] == '[')
	cupsdLogMessage(*loglevel, "%s", message);
      else
	cupsdLogMessage(*loglevel, "%s %s", sb->prefix, message);
    }
    else if (*loglevel < CUPSD_LOG_NONE && LogLevel >= CUPSD_LOG_DEBUG)
    {
      cupsdLogMessage(CUPSD_LOG_DEBUG2, "%s %s", sb->prefix, sb->buffer);
    }
  }

 /*
  * Copy the message to the line buffer...
  */

  strlcpy(line, message, (size_t)linelen);

 /*
  * Copy over the buffer data we've used up...
  */

  if (lineptr < sb->buffer + sb->bufused)
    _cups_strcpy(sb->buffer, lineptr);

  sb->bufused -= lineptr - sb->buffer;

  if (sb->bufused < 0)
    sb->bufused = 0;

  return (line);
}
