/* Print output of stream to given obstack.
   Copyright (C) 1996-2023 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library 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
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

#include <assert.h>
#include <math_ldbl_opt.h>
#include <obstack.h>
#include <printf.h>
#include <stdarg.h>
#include <printf_buffer.h>

struct __printf_buffer_obstack
{
  struct __printf_buffer base;
  struct obstack *obstack;

  /* obstack_1grow is called for compatibility reasons.  This needs
     one extra character, and this is the backing store for it.  */
  char ch;
};

void
__printf_buffer_flush_obstack (struct __printf_buffer_obstack *buf)
{
  /* About to switch buffers, so record the bytes written so far.  */
  buf->base.written += buf->base.write_ptr - buf->base.write_base;

  if (buf->base.write_ptr == &buf->ch + 1)
    {
      /* Errors are reported via a callback mechanism (presumably for
	 process termination).  */
      obstack_1grow (buf->obstack, buf->ch);
      buf->base.write_base = obstack_next_free (buf->obstack);
      buf->base.write_ptr = buf->base.write_base;
      size_t size = obstack_room (buf->obstack);
      buf->base.write_end = buf->base.write_ptr + size;
      /* Reserve the space on the obstack size.  */
      obstack_blank_fast (buf->obstack, size);
    }
  else
    {
      /* Obtain the extra character.  */
      buf->base.write_base = &buf->ch;
      buf->base.write_ptr = &buf->ch;
      buf->base.write_end = &buf->ch + 1;
    }
}

int
__obstack_vprintf_internal (struct obstack *obstack, const char *format,
			    va_list args, unsigned int mode_flags)
{
  /* Legacy setup code for compatibility.  */
  size_t room = obstack_room (obstack);
  size_t size = obstack_object_size (obstack) + room;
  if (size == 0)
    {
      /* Get more memory.  */
      obstack_make_room (obstack, 64);

      /* Recompute how much room we have.  */
      room = obstack_room (obstack);
      size = room;

      assert (size != 0);
    }

  struct __printf_buffer_obstack buf;
  {
    /* The obstack write location might be in the middle of an object.  */
    char *ptr = obstack_next_free (obstack);
    char *end = obstack_base (obstack) + size;
    __printf_buffer_init (&buf.base, ptr, end - ptr,
			  __printf_buffer_mode_obstack);
  }
  buf.obstack = obstack;

  /* Now allocate the rest of the current chunk.  */
  obstack_blank_fast (obstack, room);

  __printf_buffer (&buf.base, format, args, mode_flags);

  if (buf.base.write_ptr == &buf.ch + 1)
    /* buf.ch is in use.  Put it into the obstack.  */
    obstack_1grow (buf.obstack, buf.ch);
  else if (buf.base.write_ptr != &buf.ch)
    /* Shrink the buffer to the space we really currently need.  */
    obstack_blank_fast (buf.obstack, buf.base.write_ptr - buf.base.write_end);

  return __printf_buffer_done (&buf.base);
}

int
__obstack_vprintf (struct obstack *obstack, const char *format, va_list ap)
{
  return __obstack_vprintf_internal (obstack, format, ap, 0);
}
ldbl_weak_alias (__obstack_vprintf, obstack_vprintf)

int
__obstack_printf (struct obstack *obstack, const char *format, ...)
{
  int result;
  va_list ap;
  va_start (ap, format);
  result = __obstack_vprintf_internal (obstack, format, ap, 0);
  va_end (ap);
  return result;
}
ldbl_weak_alias (__obstack_printf, obstack_printf)
