[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [MiNT] Gcc 3.3.6 v 4.0.1



Done, thanks Vincent !

Alan.

On Fri, 2009-03-06 at 01:01 +0100, Vincent Rivière wrote:
> So here is my patch for the file obstream.c in the MiNTLib. The previous 
> code was totally broken. Hopefully that file is independant to the remaining 
> of the MiNTLib. So this patch is not going to break anything. I added a 
> testcase, called test-obstack, which checks for common scenarios. Everything 
> seems to be OK now.
> 
> This patch fixes the t-printf testcase in the GMP library. Remember, it was 
> the main goal. Now all the GMP test cases passes ! It uses very intensive 
> mathematical functions, so our platform is pretty reliable :-)
> 
> Note that the test-printf testcase in the MiNTLib currently fails, it is 
> absolutely not related to this patch.
> 
> Some background information:
> 
> Obstacks are memory pools. They are part of glibc.
> http://www.gnu.org/software/libc/manual/html_node/Obstacks.html
> 
> The function obstack_printf() is similar to fprintf, except it prints into 
> an obstack growing object.
> http://www.gnu.org/software/libc/manual/html_node/Dynamic-Output.html
> 
> The function open_obstack_stream() allows to write to an obstack growing 
> object through a FILE interface (fwrite, fprintf...)
> http://www.gnu.org/software/libc/manual/html_node/Obstack-Streams.html
> This function is still documented, but I have not been able to find it, 
> either in the glibc sources neither in my Linux boxes.
> 
> Please apply and commit this patch !
> 
> plain text document attachment (obstream.patch)
> diff -aurN mintlib-CVS-20090305/stdio/Makefile mintlib-CVS-20090305-obstream/stdio/Makefile
> --- mintlib-CVS-20090305/stdio/Makefile	2009-03-05 22:50:13.890500000 +0100
> +++ mintlib-CVS-20090305-obstream/stdio/Makefile	2009-03-01 23:23:04.000000000 +0100
> @@ -31,7 +31,7 @@
>  
>  TESTS = bug1 bug2 bug3 bug4 bug5 bug6 bug7 bug8 bug9 bug10 bug11 bug12 \
>  doprnt errnobug ferror fformat fileno fseek fwrite getln glue iformat \
> -llformat popen printf printfsz rdwr scanf scanf1 scanf2 \
> +llformat obstream popen printf printfsz rdwr scanf scanf1 scanf2 \
>  scanf3 scanf4 scanf5 scanf6 scanf7 scanf8 scanf9 scanf10 scanf11 scanf12 \
>  stdiomisc temp tmpfile tmpnam ungetc wc-printf xbug
>  
> diff -aurN mintlib-CVS-20090305/stdio/obstream.c mintlib-CVS-20090305-obstream/stdio/obstream.c
> --- mintlib-CVS-20090305/stdio/obstream.c	2009-03-05 22:50:13.890500000 +0100
> +++ mintlib-CVS-20090305-obstream/stdio/obstream.c	2009-03-05 22:31:14.000000000 +0100
> @@ -1,4 +1,4 @@
> -/* Copyright (C) 1992, 1996, 1997 Free Software Foundation, Inc.
> +/* Copyright (C) 1992, 1996, 1997, 2009 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
> @@ -20,60 +20,64 @@
>  #include <obstack.h>
>  #include <stdarg.h>
>  #include <string.h>
> +#include <stdlib.h>
>  
>  /* Output-room function for obstack streams.  */
>  
>  static void
>  grow (FILE *stream, int c)
>  {
> -  struct obstack *const obstack = (struct obstack *) stream->__cookie;
> +  struct obstack *obstack = (struct obstack *) stream->__cookie;
> +  int size_written = (int)(stream->__target + stream->__bufp - stream->__buffer);
>  
> -  /* Move the end of the object back to include only the portion
> -     of the buffer which the user has already written into.  */
> -  obstack_blank_fast (obstack, - (stream->__put_limit - stream->__bufp));
> -
> -  if ((size_t) stream->__target > obstack_object_size (obstack))
> -    {
> -      /* Our target (where the buffer maps to) is always zero except when
> -	 the user just did a SEEK_END fseek.  If he sought within the
> -	 buffer, we need do nothing and will zero the target below.  If he
> -	 sought past the end of the object, grow and zero-fill the object
> -	 up to the target address.  */
> -
> -      obstack_blank (obstack,
> -		     stream->__target - obstack_object_size (obstack));
> -      /* fseek has just flushed us, so the put limit points
> -	 to the end of the written data.  */
> -      bzero (stream->__put_limit,
> -	     stream->__target - stream->__bufsize);
> +  /* Check if the buffer has been flushed by fseek().  */
> +  if (stream->__target != -1 && stream->__target > 0)
> +    {
> +      /* Restore the stream pointers to match the object.  */
> +      stream->__buffer = obstack_base (obstack);
> +      stream->__bufsize = obstack_object_size (obstack);
> +      stream->__bufp = stream->__buffer + stream->__target;
> +      stream->__get_limit = stream->__bufp;
> +      stream->__put_limit = stream->__buffer + stream->__bufsize;
> +      stream->__target = 0;
>      }
>  
> -  if (c != EOF)
> -    obstack_1grow (obstack, (unsigned char) c);
> -
>    /* The stream buffer always maps exactly to the object on the top
>       of the obstack.  The start of the buffer is the start of the object.
>       The put limit points just past the end of the object.  On fflush, the
>       obstack is sync'd so the end of the object points just past the last
>       character written to the stream.  */
> -
> -  stream->__target = stream->__offset = 0;
> -  stream->__buffer = obstack_base (obstack);
> -  stream->__bufsize = obstack_room (obstack);
> -  stream->__bufp = obstack_next_free (obstack);
> -  stream->__get_limit = stream->__bufp;
> -
>    if (c == EOF)
> -    /* This is fflush.  Make the stream buffer, the object,
> -       and the characters actually written all match.  */
> -    stream->__put_limit = stream->__get_limit;
> -  else
>      {
> -      /* Extend the buffer (and the object) to include
> -	 the rest of the obstack chunk (which is uninitialized).
> -	 Data past bufp is undefined.  */
> +      /* This is fflush. The object must be shrinked to keep only the portion
> +         of the buffer which the user has already written into.  */
> +      obstack_blank_fast (obstack, -(obstack_object_size (obstack) - size_written));
> +
> +      /* Adjust the stream pointers.  */
> +      stream->__bufsize = obstack_object_size (obstack);
>        stream->__put_limit = stream->__buffer + stream->__bufsize;
> -      obstack_blank_fast (obstack, stream->__put_limit - stream->__bufp);
> +    }
> +  else if (size_written == obstack_object_size (obstack))
> +    {
> +      /* The buffer is full. Appending a byte to the object
> +         may cause the allocation of a new chunk.  */
> +      obstack_1grow (obstack, (unsigned char) c);
> +      ++size_written;
> +
> +      /* Increase the object size to the size of the new chunk.  */
> +      obstack_blank_fast (obstack, obstack_room (obstack));
> +
> +      /* Relocate the stream pointers.  */
> +      stream->__buffer = obstack_base (obstack);
> +      stream->__bufsize = obstack_object_size (obstack);
> +      stream->__bufp = stream->__buffer + size_written;
> +      stream->__get_limit = stream->__bufp;
> +      stream->__put_limit = stream->__buffer + stream->__bufsize;
> +    }
> +  else
> +    {
> +      /* The user called fseek() backwards, so there is room in the buffer.  */
> +      *stream->__bufp++ = (unsigned char)c;
>      }
>  }
>  
> @@ -83,56 +87,51 @@
>  static int
>  seek (void *cookie, fpos_t *pos, int whence)
>  {
> +  struct obstack *obstack = (struct obstack *) cookie;
> +  fpos_t current_offset = obstack_object_size (obstack); /* Stream has just been flushed.  */
> +  fpos_t target_offset;
> +  ptrdiff_t delta;
> +
>    switch (whence)
>      {
>      case SEEK_SET:
> +      target_offset = *pos;
> +      break;
> +
>      case SEEK_CUR:
> -      return 0;
> +      target_offset = current_offset + *pos;
> +      break;
>  
>      case SEEK_END:
> -      /* Return the position relative to the end of the object.
> -	 fseek has just flushed us, so the obstack is consistent.  */
> -      *pos += obstack_object_size ((struct obstack *) cookie);
> -      return 0;
> +      target_offset = current_offset - *pos;
> +      break;
>  
>      default:
>        __libc_fatal ("obstream::seek called with bogus WHENCE\n");
>        return -1;
>      }
> -}
> -
> -/* Input room function for obstack streams.
> -   Only what has been written to the stream can be read back.  */
> -
> -static int
> -input (FILE *stream)
> -{
> -  /* Re-sync with the obstack, growing the object if necessary.  */
> -  grow (stream, EOF);
>  
> -  if (stream->__bufp < stream->__get_limit)
> -    return (unsigned char) *stream->__bufp++;
> +  /* Resize the buffer.  */
> +  delta = target_offset - current_offset;
> +  if (delta > 0)
> +    {
> +      obstack_blank (obstack, delta);
> +      bzero (obstack_base (obstack) + current_offset, delta);
> +    }
>  
> -  stream->__eof = 1;
> -  return EOF;
> +  return 0;
>  }
> -
> +
>  /* Initialize STREAM to talk to OBSTACK.  */
>  
>  static void
>  init_obstream (FILE *stream, struct obstack *obstack)
>  {
> -  (void) obstack;
> +  int initial_object_size;
>  
> +  stream->__cookie = obstack;
>    stream->__magic = _IOMAGIC;
>    stream->__mode.__write = 1;
> -  stream->__mode.__read = 1;
> -
> -  /* Input can read only what has been written.  */
> -  stream->__room_funcs.__input = input;
> -
> -  /* Do nothing for close.  */
> -  stream->__io_funcs.__close = NULL;
>  
>    /* When the buffer is full, grow the obstack.  */
>    stream->__room_funcs.__output = grow;
> @@ -141,20 +140,26 @@
>    stream->__io_funcs.__seek = seek;
>    stream->__target = stream->__offset = 0;
>  
> +  /* Increase the size of the current object to the size of the chunk.  */
> +  initial_object_size = obstack_object_size (obstack);
> +  obstack_blank_fast (obstack, obstack_room (obstack));
> +
> +  /* The initial buffer is the current growing object.  */
> +  stream->__buffer = obstack_base (obstack);
> +  stream->__bufsize = obstack_object_size (obstack);
> +  stream->__bufp = stream->__buffer + initial_object_size;
> +  stream->__get_limit = stream->__bufp;
> +  stream->__put_limit = stream->__buffer + stream->__bufsize;
>    stream->__seen = 1;
>  
>    /* Don't deallocate that buffer!  */
>    stream->__userbuf = 1;
> -
> -  /* We don't have to initialize the buffer.
> -     The first read attempt will call grow, which will do all the work.  */
>  }
>  
>  FILE *
> -open_obstack_stream (obstack)
> -     struct obstack *obstack;
> +open_obstack_stream (struct obstack *obstack)
>  {
> -  register FILE *stream;
> +  FILE *stream;
>  
>    stream = __newstream ();
>    if (stream == NULL)
> @@ -165,15 +170,18 @@
>  }
>  
>  int
> -obstack_vprintf (obstack, format, args)
> -      struct obstack *obstack;
> -      const char *format;
> -      va_list args;
> +obstack_vprintf (struct obstack *obstack, const char *format, va_list args)
>  {
> +  int result;
>    FILE f;
>    bzero (&f, sizeof (f));
>    init_obstream (&f, obstack);
> -  return vfprintf (&f, format, args);
> +  result = vfprintf (&f, format, args);
> +
> +  if (result >= 0)
> +    fflush(&f);
> +
> +  return result;
>  }
>  
>  int
> diff -aurN mintlib-CVS-20090305/stdio/test-obstream.c mintlib-CVS-20090305-obstream/stdio/test-obstream.c
> --- mintlib-CVS-20090305/stdio/test-obstream.c	1970-01-01 01:00:00.000000000 +0100
> +++ mintlib-CVS-20090305-obstream/stdio/test-obstream.c	2009-03-05 22:26:00.000000000 +0100
> @@ -0,0 +1,359 @@
> +/* Copyright (C) 2009 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 Library General Public License as
> +   published by the Free Software Foundation; either version 2 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
> +   Library General Public License for more details.
> +
> +   You should have received a copy of the GNU Library General Public
> +   License along with the GNU C Library; see the file COPYING.LIB.  If not,
> +   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> +   Boston, MA 02111-1307, USA.  */
> +
> +#include <stdio.h>
> +#include <obstack.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <stdarg.h>
> +
> +#define obstack_chunk_alloc malloc
> +#define obstack_chunk_free  free
> +
> +static void
> +check_obstack_printf_int(void)
> +{
> +  const char *fmt, *want;
> +  struct obstack ob;
> +  int got_len, want_len, ob_len;
> +  char *got;
> +
> +  obstack_init (&ob);
> +
> +  fmt = "%d";
> +  want = "567";
> +  want_len = strlen (want);
> +  got_len = obstack_printf (&ob, fmt, 567);
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +
> +  if (got_len != want_len
> +      || ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_obstack_printf_int: obstack_printf wrong\n");
> +      printf ("  fmt      |%s|\n", fmt);
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", got_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  got_len  %d\n", got_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  obstack_free (&ob, NULL);
> +}
> +
> +static void
> +check_obstack_printf_typical(void)
> +{
> +  const char *fmt, *want;
> +  struct obstack ob;
> +  int got_len, want_len;
> +  char *got;
> +
> +  obstack_init (&ob);
> +
> +  fmt = "Found %d occurences.";
> +  want = "Found 23 occurences.";
> +  want_len = strlen (want);
> +  obstack_printf (&ob, fmt, 23);
> +  obstack_1grow(&ob, '\0');
> +  got = (char*)obstack_finish (&ob);
> +  got_len = strlen(got);
> +
> +  if (got_len != want_len
> +      || strcmp(got, want) != 0)
> +    {
> +      printf ("check_obstack_printf_typical: obstack_printf wrong\n");
> +      printf ("  fmt      |%s|\n", fmt);
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", got_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  got_len  %d\n", got_len);
> +      exit (1);
> +    }
> +
> +  obstack_free (&ob, NULL);
> +}
> +
> +static void
> +check_obstack_printf_two_times(void)
> +{
> +  const char *fmt, *want;
> +  struct obstack ob;
> +  int got_len, want_len, ob_len;
> +  char *got;
> +
> +  obstack_init (&ob);
> +
> +  /* First write */
> +  fmt = "%d";
> +  want = "123";
> +  want_len = strlen (want);
> +  got_len = obstack_printf (&ob, fmt, 123);
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +
> +  if (got_len != want_len
> +      || ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_obstack_printf_two_times: first obstack_printf wrong\n");
> +      printf ("  fmt      |%s|\n", fmt);
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", got_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  got_len  %d\n", got_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  /* Second write */
> +  fmt = "%d";
> +  want = "1234567";
> +  want_len = strlen (want);
> +  got_len = obstack_printf (&ob, fmt, 4567);
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +
> +  if (got_len != 4
> +      || ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_obstack_printf_two_times: second obstack_printf wrong\n");
> +      printf ("  fmt      |%s|\n", fmt);
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", got_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  got_len  %d\n", got_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  obstack_free (&ob, NULL);
> +}
> +
> +static void
> +check_obstack_vprintf(const char* want, const char* fmt, ...)
> +{
> +  va_list ap;
> +  struct obstack ob;
> +  int got_len, want_len, ob_len;
> +  char *got;
> +
> +  obstack_init (&ob);
> +
> +  va_start (ap, fmt);
> +  want_len = strlen (want);
> +  got_len = obstack_vprintf (&ob, fmt, ap);
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +
> +  if (got_len != want_len
> +      || ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_obstack_vprintf: obstack_printf wrong\n");
> +      printf ("  fmt      |%s|\n", fmt);
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", got_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  got_len  %d\n", got_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  obstack_free (&ob, NULL);
> +}
> +
> +static void
> +check_open_obstack_stream(void)
> +{
> +  struct obstack ob;
> +  const char* fmt;
> +  int got_len, want_len, ob_len, got2_len, want2_len, err;
> +  const char *want, *got, *want2;
> +  FILE* f;
> +
> +  obstack_init (&ob);
> +
> +  f = open_obstack_stream(&ob);
> +  if (f == NULL)
> +    {
> +      printf("check_open_obstack_stream: open_obstack_stream returned NULL\n");
> +      exit (1);
> +    }
> +
> +  /* Test fprintf */
> +  fmt = "one %d";
> +  want = "one 2";
> +  want_len = strlen (want);
> +  got_len = fprintf(f, fmt, 2);
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +  if (got_len != want_len
> +      || ob_len < want_len /* Will be equal after flush.  */
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_open_obstack_stream: fprintf wrong\n");
> +      printf ("  fmt      |%s|\n", fmt);
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", got_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  got_len  %d\n", got_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  /* Test fwrite */
> +  want = "one 2 three\nfour";
> +  want_len = strlen (want);
> +  want2 = " three\nfour";
> +  want2_len = strlen (want2);
> +  got2_len = fwrite(want2, 1, want2_len, f);
> +  if (got2_len != want2_len)
> +    {
> +      printf("check_open_obstack_stream: fwrite failed.\n");
> +      exit (1);
> +    }
> +
> +  /* Test fflush */
> +  err = fflush(f);
> +  if (err != 0)
> +    {
> +      printf("check_open_obstack_stream: fflush failed.\n");
> +      exit (1);
> +    }
> +
> +  /* Check fwrite + fflush */
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +  if (got2_len != want2_len
> +      || ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_open_obstack_stream: fwrite wrong\n");
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", ob_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      printf ("  want2_len %d\n", want2_len);
> +      printf ("  got2_len  %d\n", got2_len);
> +      exit (1);
> +    }
> +
> +  /* Test fseek SEEK_SET */
> +  err = fseek(f, 25, SEEK_SET);
> +  if (err != 0)
> +    {
> +      printf("check_open_obstack_stream: fseek SEEK_SET failed.\n");
> +      exit (1);
> +    }
> +
> +  want = "one 2 three\nfour\0\0\0\0\0\0\0\0\0";
> +  want_len = 25;
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +  if (ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_open_obstack_stream: fseek SEEK_SET wrong\n");
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", ob_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  /* Test fseek SEEK_CUR backwards.  */
> +  err = fseek(f, -11, SEEK_CUR);
> +  if (err != 0)
> +    {
> +      printf("check_open_obstack_stream: fseek SEEK_CUR failed.\n");
> +      exit (1);
> +    }
> +
> +  /* Seeking backwards should not change the object contents.  */
> +  want = "one 2 three\nfour\0\0\0\0\0\0\0\0\0";
> +  want_len = 25;
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +  if (ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_open_obstack_stream: fseek SEEK_CUR wrong\n");
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", ob_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      exit (1);
> +    }
> +
> +  /* Test fwrite2 */
> +  want = "one 2 three\nfoObar five six seven";
> +  want_len = strlen (want);
> +  want2 = "Obar five six seven";
> +  want2_len = strlen (want2);
> +  got2_len = fwrite(want2, 1, want2_len, f);
> +  if (got2_len != want2_len)
> +    {
> +      printf("check_open_obstack_stream: fwrite2 failed.\n");
> +      exit (1);
> +    }
> +
> +  /* Test fclose */
> +  err = fclose(f);
> +  if (err != 0)
> +    {
> +      printf("check_open_obstack_stream: fclose failed.\n");
> +      exit (1);
> +    }
> +
> +  /* Check fwrite + fclose */
> +  got = obstack_base (&ob);
> +  ob_len = obstack_object_size (&ob);
> +  if (got2_len != want2_len
> +      || ob_len != want_len
> +      || memcmp (got, want, want_len) != 0)
> +    {
> +      printf ("check_open_obstack_stream: fwrite2 wrong\n");
> +      printf ("  want     |%s|\n", want);
> +      printf ("  got      |%.*s|\n", ob_len, got);
> +      printf ("  want_len %d\n", want_len);
> +      printf ("  ob_len   %d\n", ob_len);
> +      printf ("  want2_len %d\n", want2_len);
> +      printf ("  got2_len  %d\n", got2_len);
> +      exit (1);
> +    }
> +
> +  obstack_free (&ob, NULL);
> +}
> +
> +int
> +main (int argc, char *argv[])
> +{
> +  check_obstack_printf_int();
> +  check_obstack_printf_typical();
> +  check_obstack_printf_two_times();
> +  check_obstack_vprintf("one and 2 then 3.4", "%s and %d then %.1f", "one", 2, 3.4);
> +  check_obstack_vprintf("one\ntwo\nthree", "one\n%s", "two\nthree");
> +  check_open_obstack_stream();
> +
> +  return 0;
> +}