[v3,3/3] libio: fmemopen rewrite to POSIX compliance

Message ID 53B3FB30.6010201@linux.vnet.ibm.com
State Superseded
Delegated to: Adhemerval Zanella Netto
Headers

Commit Message

Adhemerval Zanella Netto July 2, 2014, 12:29 p.m. UTC
  Hi,

This is a small update on the previous fmemopen patch I have sent [1].
the change is basically:

* 'w' mode does not truncate the buffer, only 'w+'

I also rebased against master to adjust NEWS file.

[1] https://sourceware.org/ml/libc-alpha/2014-06/msg00878.html

--

	[BZ #6544]
	[BZ #11216]
	[BZ #12836]
	[BZ #13151]
	[BZ #13152]
	[BZ #14292]
	* include/stdio.h (fmemopen): Remove hidden prototype.
	(__fmemopen): Add new hidden prototype.
	* libio/Makefile: Add oldfmemopen object.
	* libio/Versions [GLIBC_2.20]: Add new fmemopen symbol.
	* libio/fmemopen.c (__fmemopen): Function rewrite to be POSIX
	compliance.
	* libio/oldfmemopen.c: New file: old fmemopen implementation for
	symbol compatibility.
	* stdio-common/Makefile [tests]: Add new tst-fmemopen3.
	* stdio-common/psiginfo.c [psiginfo]: Call __fmemopen instead of
	fmemopen.
	* stdio-common/tst-fmemopen3.c: New file: more fmemopen tests, focus
	on append and read mode.
	* sysdeps/unix/sysv/linux/aarch64/nptl/libc.abilist [GLIBC_2.20]: Add
	fmemopen.
	* sysdeps/unix/sysv/linux/alpha/libc.abilist [GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/arm/libc.abilist [GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/i386/libc.abilist [GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/m68k/coldfire/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/m68k/m680x0/nptl/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/microblaze/nptl/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/fpu/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n32/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/mips/mips64/n64/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/powerpc/powerpc64/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-32/nptl/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/s390/s390-64/nptl/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/sh/nptl/libc.abilist [GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc32/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/sparc/sparc64/nptl/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
	[GLIBC_2.20]: Likewise.
	* sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist [GLIBC_2.20]:
	Likewise.
	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist [GLIBC_2.20]:
	Likewise.

---
  

Comments

Adhemerval Zanella Netto July 7, 2014, 6 p.m. UTC | #1
ping.

On 02-07-2014 09:29, Adhemerval Zanella wrote:
> Hi,
>
> This is a small update on the previous fmemopen patch I have sent [1].
> the change is basically:
>
> * 'w' mode does not truncate the buffer, only 'w+'
>
> I also rebased against master to adjust NEWS file.
>
> [1] https://sourceware.org/ml/libc-alpha/2014-06/msg00878.html
>
> --
>
> 	[BZ #6544]
> 	[BZ #11216]
> 	[BZ #12836]
> 	[BZ #13151]
> 	[BZ #13152]
> 	[BZ #14292]
> 	* include/stdio.h (fmemopen): Remove hidden prototype.
> 	(__fmemopen): Add new hidden prototype.
> 	* libio/Makefile: Add oldfmemopen object.
> 	* libio/Versions [GLIBC_2.20]: Add new fmemopen symbol.
> 	* libio/fmemopen.c (__fmemopen): Function rewrite to be POSIX
> 	compliance.
> 	* libio/oldfmemopen.c: New file: old fmemopen implementation for
> 	symbol compatibility.
> 	* stdio-common/Makefile [tests]: Add new tst-fmemopen3.
> 	* stdio-common/psiginfo.c [psiginfo]: Call __fmemopen instead of
> 	fmemopen.
> 	* stdio-common/tst-fmemopen3.c: New file: more fmemopen tests, focus
> 	on append and read mode.
> 	* sysdeps/unix/sysv/linux/aarch64/nptl/libc.abilist [GLIBC_2.20]: Add
> 	fmemopen.
> 	* sysdeps/unix/sysv/linux/alpha/libc.abilist [GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/arm/libc.abilist [GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/i386/libc.abilist [GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/m68k/coldfire/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/m68k/m680x0/nptl/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/microblaze/nptl/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips32/fpu/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips64/n32/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/mips/mips64/n64/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/s390/s390-32/nptl/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/s390/s390-64/nptl/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/sh/nptl/libc.abilist [GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/sparc/sparc32/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/sparc/sparc64/nptl/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> 	[GLIBC_2.20]: Likewise.
> 	* sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist [GLIBC_2.20]:
> 	Likewise.
> 	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist [GLIBC_2.20]:
> 	Likewise.
>
> ---
>
> diff --git a/NEWS b/NEWS
> index a07ea66..7a0851e 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -9,19 +9,20 @@ Version 2.20
>
>  * The following bugs are resolved with this release:
>
> -  6804, 9894, 12994, 13347, 13651, 14308, 14770, 15119, 15132, 15347, 15514,
> -  15698, 15804, 15894, 15946, 16002, 16064, 16095, 16198, 16284, 16287,
> -  16315, 16348, 16349, 16354, 16357, 16362, 16447, 16516, 16532, 16539,
> -  16545, 16561, 16562, 16564, 16574, 16599, 16600, 16609, 16610, 16611,
> -  16613, 16619, 16623, 16629, 16632, 16634, 16639, 16642, 16648, 16649,
> -  16670, 16674, 16677, 16680, 16681, 16683, 16689, 16695, 16701, 16706,
> -  16707, 16712, 16713, 16714, 16724, 16731, 16739, 16740, 16743, 16754,
> -  16758, 16759, 16760, 16770, 16786, 16789, 16791, 16796, 16799, 16800,
> -  16815, 16823, 16824, 16831, 16838, 16849, 16854, 16876, 16877, 16878,
> -  16882, 16885, 16888, 16890, 16912, 16915, 16916, 16917, 16918, 16922,
> -  16927, 16928, 16932, 16943, 16958, 16965, 16966, 16967, 16977, 16978,
> -  16984, 16990, 16996, 17009, 17022, 17031, 17042, 17048, 17050, 17058,
> -  17061, 17062, 17069, 17075, 17079, 17084, 17086, 17092, 17097.
> +  6544, 6804, 9894, 11216, 12836, 12994, 13151, 13152, 13347, 13651, 14292,
> +  14308, 14770, 15119, 15132, 15347, 15514, 15698, 15804, 15894, 15946,
> +  16002, 16064, 16095, 16198, 16284, 16287, 16315, 16348, 16349, 16354,
> +  16357, 16362, 16447, 16516, 16532, 16539, 16545, 16561, 16562, 16564,
> +  16574, 16599, 16600, 16609, 16610, 16611, 16613, 16619, 16623, 16629,
> +  16632, 16634, 16639, 16642, 16648, 16649, 16670, 16674, 16677, 16680,
> +  16681, 16683, 16689, 16695, 16701, 16706, 16707, 16712, 16713, 16714,
> +  16724, 16731, 16739, 16740, 16743, 16754, 16758, 16759, 16760, 16770,
> +  16786, 16789, 16791, 16796, 16799, 16800, 16815, 16823, 16824, 16831,
> +  16838, 16849, 16854, 16876, 16877, 16878, 16882, 16885, 16888, 16890,
> +  16912, 16915, 16916, 16917, 16918, 16922, 16927, 16928, 16932, 16943,
> +  16958, 16965, 16966, 16967, 16977, 16978, 16984, 16990, 16996, 17009,
> +  17022, 17031, 17042, 17048, 17050, 17058, 17061, 17062, 17069, 17075,
> +  17079, 17084, 17086, 17092, 17097.
>
>  * Optimized strchr implementation for AArch64.  Contributed by ARM Ltd.
>
> diff --git a/include/stdio.h b/include/stdio.h
> index 9f2ea31..94ba5a5 100644
> --- a/include/stdio.h
> +++ b/include/stdio.h
> @@ -152,7 +152,6 @@ libc_hidden_proto (fread_unlocked)
>  libc_hidden_proto (fwrite_unlocked)
>  libc_hidden_proto (fgets_unlocked)
>  libc_hidden_proto (fputs_unlocked)
> -libc_hidden_proto (fmemopen)
>  libc_hidden_proto (open_memstream)
>  libc_hidden_proto (__libc_fatal)
>  libc_hidden_proto (__vsprintf_chk)
> @@ -180,6 +179,9 @@ gets (char *__str)
>  }
>  #  endif
>
> +extern FILE * __fmemopen (void *buf, size_t len, const char *mode);
> +libc_hidden_proto (__fmemopen)
> +
>  __END_DECLS
>  # endif
>
> diff --git a/libio/Makefile b/libio/Makefile
> index 56952ce..c76b37e 100644
> --- a/libio/Makefile
> +++ b/libio/Makefile
> @@ -46,7 +46,7 @@ routines	:=							      \
>  	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
>  	__fpurge __fpending __fsetlocking				      \
>  									      \
> -	libc_fatal fmemopen
> +	libc_fatal fmemopen oldfmemopen
>
>  tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
>  	tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
> diff --git a/libio/Versions b/libio/Versions
> index 8df89d2..7ffa64d 100644
> --- a/libio/Versions
> +++ b/libio/Versions
> @@ -148,6 +148,10 @@ libc {
>    GLIBC_2.4 {
>      open_wmemstream;
>    }
> +  GLIBC_2.20 {
> +    # f*
> +    fmemopen;
> +  }
>    GLIBC_PRIVATE {
>      # Used by NPTL and librt
>      __libc_fatal;
> diff --git a/libio/fmemopen.c b/libio/fmemopen.c
> index aee2696..53e9d57 100644
> --- a/libio/fmemopen.c
> +++ b/libio/fmemopen.c
> @@ -1,7 +1,6 @@
> -/* Fmemopen implementation.
> -   Copyright (C) 2000-2014 Free Software Foundation, Inc.
> +/* fmemopen implementation.
> +   Copyright (C) 2014 Free Software Foundation, Inc.
>     This file is part of the GNU C Library.
> -   Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
>
>     The GNU C Library is free software; you can redistribute it and/or
>     modify it under the terms of the GNU Lesser General Public
> @@ -17,54 +16,10 @@
>     License along with the GNU C Library; if not, see
>     <http://www.gnu.org/licenses/>.  */
>
> -/*
> - * fmemopen() - "my" version of a string stream
> - * Hanno Mueller, kontakt@hanno.de
> - *
> - *
> - * I needed fmemopen() for an application that I currently work on,
> - * but couldn't find it in libio. The following snippet of code is an
> - * attempt to implement what glibc's documentation describes.
> - *
> - *
> - *
> - * I already see some potential problems:
> - *
> - * - I never used the "original" fmemopen(). I am sure that "my"
> - *   fmemopen() behaves differently than the original version.
> - *
> - * - The documentation doesn't say wether a string stream allows
> - *   seeks. I checked the old fmemopen implementation in glibc's stdio
> - *   directory, wasn't quite able to see what is going on in that
> - *   source, but as far as I understand there was no seek there. For
> - *   my application, I needed fseek() and ftell(), so it's here.
> - *
> - * - "append" mode and fseek(p, SEEK_END) have two different ideas
> - *   about the "end" of the stream.
> - *
> - *   As described in the documentation, when opening the file in
> - *   "append" mode, the position pointer will be set to the first null
> - *   character of the string buffer (yet the buffer may already
> - *   contain more data). For fseek(), the last byte of the buffer is
> - *   used as the end of the stream.
> - *
> - * - It is unclear to me what the documentation tries to say when it
> - *   explains what happens when you use fmemopen with a NULL
> - *   buffer.
> - *
> - *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
> - *   is really only useful if you are going to write things to the
> - *   buffer and then read them back in again."
> - *
> - *   What does that mean if the original fmemopen() did not allow
> - *   seeking? How do you read what you just wrote without seeking back
> - *   to the beginning of the stream?
> - *
> - * - I think there should be a second version of fmemopen() that does
> - *   not add null characters for each write. (At least in my
> - *   application, I am not actually using strings but binary data and
> - *   so I don't need the stream to add null characters on its own.)
> - */
> +/* fmemopen() from 2.20 and forward works as defined by POSIX.  It also
> +   provides an older symbol, version 2.2.5, that behaves different regarding
> +   SEEK_END (libio/oldfmemopen.c).  */
> +
>
>  #include <errno.h>
>  #include <libio.h>
> @@ -79,25 +34,23 @@
>  typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
>  struct fmemopen_cookie_struct
>  {
> -  char *buffer;
> -  int mybuffer;
> -  int binmode;
> -  size_t size;
> -  _IO_off64_t pos;
> -  size_t maxpos;
> +  char        *buffer;   /* memory buffer.  */
> +  int         mybuffer;  /* own allocated buffer?  */
> +  int         append;    /* buffer openened for append?  */
> +  size_t      size;      /* buffer length in bytes.  */
> +  _IO_off64_t pos;       /* current position at the buffer.  */
> +  size_t      maxpos;    /* max position in buffer.  */
>  };
>
>
>  static ssize_t
>  fmemopen_read (void *cookie, char *b, size_t s)
>  {
> -  fmemopen_cookie_t *c;
> -
> -  c = (fmemopen_cookie_t *) cookie;
> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
>
> -  if (c->pos + s > c->size)
> +  if (c->pos + s > c->maxpos)
>      {
> -      if ((size_t) c->pos == c->size)
> +      if ((size_t) c->pos == c->maxpos)
>  	return 0;
>        s = c->size - c->pos;
>      }
> @@ -115,29 +68,28 @@ fmemopen_read (void *cookie, char *b, size_t s)
>  static ssize_t
>  fmemopen_write (void *cookie, const char *b, size_t s)
>  {
> -  fmemopen_cookie_t *c;
> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;;
> +  _IO_off64_t pos = c->append ? c->maxpos : c->pos;
>    int addnullc;
>
> -  c = (fmemopen_cookie_t *) cookie;
> -
> -  addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
> +  addnullc = (s == 0 || b[s - 1] != '\0');
>
> -  if (c->pos + s + addnullc > c->size)
> +  if (pos + s + addnullc > c->size)
>      {
> -      if ((size_t) (c->pos + addnullc) == c->size)
> +      if ((size_t) (pos + addnullc) >= c->size)
>  	{
>  	  __set_errno (ENOSPC);
>  	  return 0;
>  	}
> -      s = c->size - c->pos - addnullc;
> +      s = c->size - pos - addnullc;
>      }
>
> -  memcpy (&(c->buffer[c->pos]), b, s);
> +  memcpy (&(c->buffer[pos]), b, s);
>
> -  c->pos += s;
> -  if ((size_t) c->pos > c->maxpos)
> +  pos += s;
> +  if ((size_t) pos > c->maxpos)
>      {
> -      c->maxpos = c->pos;
> +      c->maxpos = pos;
>        if (addnullc)
>  	c->buffer[c->maxpos] = '\0';
>      }
> @@ -150,9 +102,7 @@ static int
>  fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>  {
>    _IO_off64_t np;
> -  fmemopen_cookie_t *c;
> -
> -  c = (fmemopen_cookie_t *) cookie;
> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
>
>    switch (w)
>      {
> @@ -165,7 +115,7 @@ fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>        break;
>
>      case SEEK_END:
> -      np = (c->binmode ? c->size : c->maxpos) - *p;
> +      np = c->maxpos + *p;
>        break;
>
>      default:
> @@ -184,9 +134,7 @@ fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>  static int
>  fmemopen_close (void *cookie)
>  {
> -  fmemopen_cookie_t *c;
> -
> -  c = (fmemopen_cookie_t *) cookie;
> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
>
>    if (c->mybuffer)
>      free (c->buffer);
> @@ -197,18 +145,11 @@ fmemopen_close (void *cookie)
>
>
>  FILE *
> -fmemopen (void *buf, size_t len, const char *mode)
> +__fmemopen (void *buf, size_t len, const char *mode)
>  {
>    cookie_io_functions_t iof;
>    fmemopen_cookie_t *c;
>
> -  if (__glibc_unlikely (len == 0))
> -    {
> -    einval:
> -      __set_errno (EINVAL);
> -      return NULL;
> -    }
> -
>    c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
>    if (c == NULL)
>      return NULL;
> @@ -231,26 +172,38 @@ fmemopen (void *buf, size_t len, const char *mode)
>        if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
>  	{
>  	  free (c);
> -	  goto einval;
> +	  __set_errno (EINVAL);
> +	  return NULL;
>  	}
>
>        c->buffer = buf;
>
> -      if (mode[0] == 'w')
> +      /* POSIX states that w+ mode should truncate the buffer.  */
> +      if (mode[0] == 'w' && mode[1] == '+')
>  	c->buffer[0] = '\0';
>
>        c->maxpos = strnlen (c->buffer, len);
>      }
>
> +
> +  /* Mode   |  starting position (cookie::pos) |          size (cookie::size)
> +   * ------ |----------------------------------|-----------------------------
> +   * read   |          beginning of the buffer |                size argument
> +   * write  |          beginning of the buffer |                         zero
> +   * append |    first null or size buffer + 1 |  first null or size argument
> +   */
> +
>    c->size = len;
>
> -  if (mode[0] == 'a')
> +  if (mode[0] == 'r')
> +    c->maxpos = len;
> +
> +  c->append = mode[0] == 'a';
> +  if (c->append)
>      c->pos = c->maxpos;
>    else
>      c->pos = 0;
>
> -  c->binmode = mode[0] != '\0' && mode[1] == 'b';
> -
>    iof.read = fmemopen_read;
>    iof.write = fmemopen_write;
>    iof.seek = fmemopen_seek;
> @@ -258,4 +211,5 @@ fmemopen (void *buf, size_t len, const char *mode)
>
>    return _IO_fopencookie (c, mode, iof);
>  }
> -libc_hidden_def (fmemopen)
> +libc_hidden_def (__fmemopen)
> +versioned_symbol (libc, __fmemopen, fmemopen, GLIBC_2_20);
> diff --git a/libio/oldfmemopen.c b/libio/oldfmemopen.c
> new file mode 100644
> index 0000000..4e85ebe
> --- /dev/null
> +++ b/libio/oldfmemopen.c
> @@ -0,0 +1,265 @@
> +/* Fmemopen implementation.
> +   Copyright (C) 2000-2014 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +   Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
> +
> +   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
> +   <http://www.gnu.org/licenses/>.  */
> +
> +/*
> + * fmemopen() - "my" version of a string stream
> + * Hanno Mueller, kontakt@hanno.de
> + *
> + *
> + * I needed fmemopen() for an application that I currently work on,
> + * but couldn't find it in libio. The following snippet of code is an
> + * attempt to implement what glibc's documentation describes.
> + *
> + *
> + *
> + * I already see some potential problems:
> + *
> + * - I never used the "original" fmemopen(). I am sure that "my"
> + *   fmemopen() behaves differently than the original version.
> + *
> + * - The documentation doesn't say wether a string stream allows
> + *   seeks. I checked the old fmemopen implementation in glibc's stdio
> + *   directory, wasn't quite able to see what is going on in that
> + *   source, but as far as I understand there was no seek there. For
> + *   my application, I needed fseek() and ftell(), so it's here.
> + *
> + * - "append" mode and fseek(p, SEEK_END) have two different ideas
> + *   about the "end" of the stream.
> + *
> + *   As described in the documentation, when opening the file in
> + *   "append" mode, the position pointer will be set to the first null
> + *   character of the string buffer (yet the buffer may already
> + *   contain more data). For fseek(), the last byte of the buffer is
> + *   used as the end of the stream.
> + *
> + * - It is unclear to me what the documentation tries to say when it
> + *   explains what happens when you use fmemopen with a NULL
> + *   buffer.
> + *
> + *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
> + *   is really only useful if you are going to write things to the
> + *   buffer and then read them back in again."
> + *
> + *   What does that mean if the original fmemopen() did not allow
> + *   seeking? How do you read what you just wrote without seeking back
> + *   to the beginning of the stream?
> + *
> + * - I think there should be a second version of fmemopen() that does
> + *   not add null characters for each write. (At least in my
> + *   application, I am not actually using strings but binary data and
> + *   so I don't need the stream to add null characters on its own.)
> + */
> +
> +#include "libioP.h"
> +
> +#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_20)
> +
> +#include <errno.h>
> +#include <libio.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <string.h>
> +#include <sys/types.h>
> +
> +
> +typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
> +struct fmemopen_cookie_struct
> +{
> +  char *buffer;
> +  int mybuffer;
> +  int binmode;
> +  size_t size;
> +  _IO_off64_t pos;
> +  size_t maxpos;
> +};
> +
> +
> +static ssize_t
> +fmemopen_read (void *cookie, char *b, size_t s)
> +{
> +  fmemopen_cookie_t *c;
> +
> +  c = (fmemopen_cookie_t *) cookie;
> +
> +  if (c->pos + s > c->size)
> +    {
> +      if ((size_t) c->pos == c->size)
> +	return 0;
> +      s = c->size - c->pos;
> +    }
> +
> +  memcpy (b, &(c->buffer[c->pos]), s);
> +
> +  c->pos += s;
> +  if ((size_t) c->pos > c->maxpos)
> +    c->maxpos = c->pos;
> +
> +  return s;
> +}
> +
> +
> +static ssize_t
> +fmemopen_write (void *cookie, const char *b, size_t s)
> +{
> +  fmemopen_cookie_t *c;
> +  int addnullc;
> +
> +  c = (fmemopen_cookie_t *) cookie;
> +
> +  addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
> +
> +  if (c->pos + s + addnullc > c->size)
> +    {
> +      if ((size_t) (c->pos + addnullc) == c->size)
> +	{
> +	  __set_errno (ENOSPC);
> +	  return 0;
> +	}
> +      s = c->size - c->pos - addnullc;
> +    }
> +
> +  memcpy (&(c->buffer[c->pos]), b, s);
> +
> +  c->pos += s;
> +  if ((size_t) c->pos > c->maxpos)
> +    {
> +      c->maxpos = c->pos;
> +      if (addnullc)
> +	c->buffer[c->maxpos] = '\0';
> +    }
> +
> +  return s;
> +}
> +
> +
> +static int
> +fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
> +{
> +  _IO_off64_t np;
> +  fmemopen_cookie_t *c;
> +
> +  c = (fmemopen_cookie_t *) cookie;
> +
> +  switch (w)
> +    {
> +    case SEEK_SET:
> +      np = *p;
> +      break;
> +
> +    case SEEK_CUR:
> +      np = c->pos + *p;
> +      break;
> +
> +    case SEEK_END:
> +      np = (c->binmode ? c->size : c->maxpos) - *p;
> +      break;
> +
> +    default:
> +      return -1;
> +    }
> +
> +  if (np < 0 || (size_t) np > c->size)
> +    return -1;
> +
> +  *p = c->pos = np;
> +
> +  return 0;
> +}
> +
> +
> +static int
> +fmemopen_close (void *cookie)
> +{
> +  fmemopen_cookie_t *c;
> +
> +  c = (fmemopen_cookie_t *) cookie;
> +
> +  if (c->mybuffer)
> +    free (c->buffer);
> +  free (c);
> +
> +  return 0;
> +}
> +
> +
> +FILE *
> +__old_fmemopen (void *buf, size_t len, const char *mode)
> +{
> +  cookie_io_functions_t iof;
> +  fmemopen_cookie_t *c;
> +
> +  if (__glibc_unlikely (len == 0))
> +    {
> +    einval:
> +      __set_errno (EINVAL);
> +      return NULL;
> +    }
> +
> +  c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
> +  if (c == NULL)
> +    return NULL;
> +
> +  c->mybuffer = (buf == NULL);
> +
> +  if (c->mybuffer)
> +    {
> +      c->buffer = (char *) malloc (len);
> +      if (c->buffer == NULL)
> +	{
> +	  free (c);
> +	  return NULL;
> +	}
> +      c->buffer[0] = '\0';
> +      c->maxpos = 0;
> +    }
> +  else
> +    {
> +      if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
> +	{
> +	  free (c);
> +	  goto einval;
> +	}
> +
> +      c->buffer = buf;
> +
> +      if (mode[0] == 'w')
> +	c->buffer[0] = '\0';
> +
> +      c->maxpos = strnlen (c->buffer, len);
> +    }
> +
> +  c->size = len;
> +
> +  if (mode[0] == 'a')
> +    c->pos = c->maxpos;
> +  else
> +    c->pos = 0;
> +
> +  c->binmode = mode[0] != '\0' && mode[1] == 'b';
> +
> +  iof.read = fmemopen_read;
> +  iof.write = fmemopen_write;
> +  iof.seek = fmemopen_seek;
> +  iof.close = fmemopen_close;
> +
> +  return _IO_fopencookie (c, mode, iof);
> +}
> +compat_symbol (libc, __old_fmemopen, fmemopen, GLIBC_2_2);
> +#endif
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 5f8e534..5971e65 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -57,7 +57,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
>  	 bug19 bug19a tst-popen2 scanf13 scanf14 scanf15 bug20 bug21 bug22 \
>  	 scanf16 scanf17 tst-setvbuf1 tst-grouping bug23 bug24 \
>  	 bug-vfprintf-nargs tst-long-dbl-fphex tst-fphex-wide tst-sprintf3 \
> -	 bug25 tst-printf-round bug26
> +	 bug25 tst-printf-round bug26 tst-fmemopen3
>
>  test-srcs = tst-unbputc tst-printf
>
> diff --git a/stdio-common/psiginfo.c b/stdio-common/psiginfo.c
> index 564d237..62a9671 100644
> --- a/stdio-common/psiginfo.c
> +++ b/stdio-common/psiginfo.c
> @@ -60,7 +60,7 @@ void
>  psiginfo (const siginfo_t *pinfo, const char *s)
>  {
>    char buf[512];
> -  FILE *fp = fmemopen (buf, sizeof (buf), "w");
> +  FILE *fp = __fmemopen (buf, sizeof (buf), "w");
>    if (fp == NULL)
>      {
>        const char *colon;
> diff --git a/stdio-common/tst-fmemopen3.c b/stdio-common/tst-fmemopen3.c
> new file mode 100644
> index 0000000..1ff0920
> --- /dev/null
> +++ b/stdio-common/tst-fmemopen3.c
> @@ -0,0 +1,206 @@
> +/* fmemopen tests for append and read mode.
> +   Copyright (C) 2014 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
> +   <http://www.gnu.org/licenses/>.  */
> +
> +#include <assert.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <sys/types.h>
> +
> +static void
> +print_buffer (const char *s, size_t n)
> +{
> +  size_t i;
> +  for (i=0; i<n; ++i)
> +    printf ("0x%02X (%c), ", s[i], s[i]);
> +}
> +
> +/* This test check append mode initial position (a/a+) based on POSIX defition
> +   (BZ#6544 and BZ#13151).  */
> +static int
> +do_test_write_append (const char *mode)
> +{
> +  char buf[32] = "testing buffer";
> +  char exp[32] = "testing bufferXX";
> +
> +  FILE *fp = fmemopen (buf, sizeof (buf), mode);
> +
> +  fflush (fp);
> +  fprintf (fp, "X");
> +  fseek (fp, 0, SEEK_SET);
> +  fprintf (fp, "X");
> +  fclose (fp);
> +
> +  if (strcmp (buf, exp) != 0)
> +    {
> +      printf ("%s: check failed: %s != %s\n", __FUNCTION__, buf, exp);
> +      return 1;
> +    }
> +
> +  return 0;
> +}
> +
> +/* This test check append mode initial position (a/a+) based on POSIX defition
> +   (BZ#6544 and BZ#13151) for buffer without null byte end.  */
> +static int
> +do_test_write_append_without_null (const char *mode)
> +{
> +  char buf[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
> +  char exp[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
> +
> +  /* If '\0' is not found in buffer, POSIX states that SEEK_SET should be
> +     the size argument.  */
> +  FILE *fp = fmemopen (buf, sizeof (buf) - 2, "a");
> +
> +  fflush (fp);
> +  fputc (0x70, fp);
> +  fseek (fp, 0, SEEK_SET);
> +  fputc (0x70, fp);
> +  fputc (0x70, fp);
> +  fclose (fp);
> +
> +  /* POSIX also states that a write operation on the stream shall not advance
> +     the current buffer size beyond the size given in fmemopen, so the string
> +     should be same.  */
> +  if (memcmp (buf, exp, sizeof (buf)) != 0)
> +    {
> +      printf ("%s: check failed: ", __FUNCTION__);
> +      print_buffer (buf, sizeof (buf));
> +      printf ("!= ");
> +      print_buffer (exp, sizeof (exp));
> +      printf ("\n");
> +      return 1;
> +    }
> +
> +  return 0;
> +}
> +
> +/* This test check for initial position and feek value for fmemopen objects
> +   opened with append mode.  */
> +static int
> +do_test_read_append (void)
> +{
> +  char buf[32] = "testing buffer";
> +  size_t buflen = strlen (buf);
> +  long fpos;
> +
> +  /* POSIX defines for 'a+' the initial position is the first null byte.  */
> +  FILE *fp = fmemopen (buf, sizeof (buf), "a+");
> +
> +  fpos = ftell (fp);
> +  if (fpos != buflen)
> +    {
> +      printf ("%s: ftell|SEEK_SET (fp) %li != strlen (%s) %li\n",
> +	      __FUNCTION__, fpos, buf, buflen);
> +      fclose (fp);
> +      return 1;
> +    }
> +
> +  fseek (fp, 0, SEEK_END);
> +
> +  if (fpos != buflen)
> +    {
> +      printf ("%s: ftell|SEEK_END (fp) %li != strlen (%s) %li\n",
> +	      __FUNCTION__, fpos, buf, buflen);
> +      fclose (fp);
> +      return 1;
> +    }
> +  fclose (fp);
> +
> +  /* Check if attempting to read past the current size, defined as strlen (buf)
> +     yield an EOF.  */
> +  fp = fmemopen (buf, sizeof (buf), "a+");
> +  if (getc(fp) != EOF)
> +    {
> +      printf ("%s: getc(fp) != EOF\n", __FUNCTION__);
> +      fclose (fp);
> +      return -1;
> +    }
> +
> +  fclose (fp);
> +
> +  return 0;
> +}
> +
> +/* This test check for fseek (SEEK_END) using negative offsets (BZ#14292).  The
> +   starting position of descriptor is different base on the opening mode.  */
> +static int
> +do_test_read_seek_neg (const char *mode, const char *expected)
> +{
> +  char buf[] = "abcdefghijklmnopqrstuvxz0123456789";
> +  char tmp[10];
> +  size_t tmps = sizeof (tmps);
> +  long offset = -11;
> +
> +  FILE *fp = fmemopen (buf, sizeof (buf), mode);
> +  fseek (fp, offset, SEEK_END);
> +  fread (tmp, tmps, 1, fp);
> +  
> +  if (memcmp (tmp, expected, tmps) != 0)
> +    {
> +      printf ("%s: fmemopen(%s) - fseek (fp, %li, SEEK_END):\n",
> +	      __FUNCTION__, mode, offset);
> +      printf ("  returned: ");
> +      print_buffer (tmp, tmps);
> +      printf ("\n");
> +      printf ("  expected: ");
> +      print_buffer (expected, tmps);
> +      printf ("\n");
> +      return 1;
> +    }
> +  
> +  fclose (fp);
> +
> +  return 0;
> +}
> +
> +static int
> +do_test_read_seek_negative (void)
> +{
> +  int ret = 0;
> +
> +  /* 'r' and 'w' modes defines the initial position at the buffer start and
> +     seek with SEEK_END shall seek relative to its size give in fmemopen
> +     call.  The expected tmp result is 0 to 9 *without* the ending null  */
> +  ret += do_test_read_seek_neg ("r", "0123456789");
> +  /* 'a+' mode sets the initial position at the first null byte in buffer and
> +    SEEK_END shall seek relative to its size as well.  The expected result is
> +    z012345678, since SEEK_END plus a+ start at '\0', not size.  */
> +  ret += do_test_read_seek_neg ("a+", "z012345678");
> +
> +  return ret;
> +}
> +
> +static int
> +do_test (void)
> +{
> +  int ret = 0;
> +
> +  ret += do_test_write_append ("a");
> +  ret += do_test_write_append_without_null ("a");
> +  ret += do_test_write_append ("a+");
> +  ret += do_test_write_append_without_null ("a+");
> +
> +  ret += do_test_read_append ();
> +
> +  ret += do_test_read_seek_negative ();
> +
> +  return ret;
> +}
> +
> +#define TEST_FUNCTION do_test ()
> +#include "../test-skeleton.c"
> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> index aeee312..6cf2ddd 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
> @@ -2081,3 +2081,6 @@ GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
>   _mcount F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> index 980e088..11f03a5 100644
> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
> @@ -1822,6 +1822,9 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.2
>   GLIBC_2.2 A
>   _IO_adjust_wcolumn F
> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
> index ce45208..0a6ef89 100644
> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
> @@ -89,6 +89,9 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.4
>   GLIBC_2.4 A
>   _Exit F
> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
> index 3cb314d..af60934 100644
> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
> @@ -2023,6 +2023,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist b/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
> index 067552d..afe6f4b 100644
> --- a/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
> @@ -1881,6 +1881,9 @@ GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
>   getunwind F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> index f06cc8e..2bc1ce4 100644
> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
> @@ -90,6 +90,9 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.4
>   GLIBC_2.4 A
>   _Exit F
> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> index 9010ea7..48c25b4 100644
> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
> @@ -1979,6 +1979,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> index 6e8d993..8b506ed 100644
> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
> @@ -2080,3 +2080,6 @@ GLIBC_2.18
>   xencrypt F
>   xprt_register F
>   xprt_unregister F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> index 1c3490c..eb7860e 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
> @@ -1951,6 +1951,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> index d8fd823..e4385d2 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
> @@ -1949,6 +1949,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> index 3e6ed35..44b4f20 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
> @@ -1947,6 +1947,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> index c7e46aa..2c00ad7 100644
> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
> @@ -1941,6 +1941,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> index f27b48b..29b9695 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
> @@ -1983,6 +1983,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> index a54382e..b57f378 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
> @@ -1989,6 +1989,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> index 195b587..7a1deff 100644
> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
> @@ -90,6 +90,9 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   _Exit F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> index 03f2e83..d23377d 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
> @@ -1985,6 +1985,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> index 4576fc8..7a4081e 100644
> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
> @@ -1881,6 +1881,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
> index a653292..51f8707 100644
> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
> @@ -1864,6 +1864,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> index 9defbdf..46490a7 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
> @@ -1975,6 +1975,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> index 35987fa..540bc1c 100644
> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
> @@ -1908,6 +1908,9 @@ GLIBC_2.2.4
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> index caf74b8..a473437 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
> @@ -2091,3 +2091,6 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> index 68d975b..16bd028 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
> @@ -2091,3 +2091,6 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
> diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> index caf74b8..a473437 100644
> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
> @@ -2091,3 +2091,6 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> index 914b590..b77feba 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
> @@ -1854,6 +1854,9 @@ GLIBC_2.2.5
>  GLIBC_2.2.6
>   GLIBC_2.2.6 A
>   __nanosleep F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>  GLIBC_2.3
>   GLIBC_2.3 A
>   __ctype_b_loc F
> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> index 0f64c8d..f638884 100644
> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
> @@ -2089,3 +2089,6 @@ GLIBC_2.17
>  GLIBC_2.18
>   GLIBC_2.18 A
>   __cxa_thread_atexit_impl F
> +GLIBC_2.20
> + GLIBC_2.20 A
> + fmemopen F
>
>
  
Adhemerval Zanella Netto July 14, 2014, 11:39 a.m. UTC | #2
ping.

On 07-07-2014 15:00, Adhemerval Zanella wrote:
> ping.
>
> On 02-07-2014 09:29, Adhemerval Zanella wrote:
>> Hi,
>>
>> This is a small update on the previous fmemopen patch I have sent [1].
>> the change is basically:
>>
>> * 'w' mode does not truncate the buffer, only 'w+'
>>
>> I also rebased against master to adjust NEWS file.
>>
>> [1] https://sourceware.org/ml/libc-alpha/2014-06/msg00878.html
>>
>> --
>>
>> 	[BZ #6544]
>> 	[BZ #11216]
>> 	[BZ #12836]
>> 	[BZ #13151]
>> 	[BZ #13152]
>> 	[BZ #14292]
>> 	* include/stdio.h (fmemopen): Remove hidden prototype.
>> 	(__fmemopen): Add new hidden prototype.
>> 	* libio/Makefile: Add oldfmemopen object.
>> 	* libio/Versions [GLIBC_2.20]: Add new fmemopen symbol.
>> 	* libio/fmemopen.c (__fmemopen): Function rewrite to be POSIX
>> 	compliance.
>> 	* libio/oldfmemopen.c: New file: old fmemopen implementation for
>> 	symbol compatibility.
>> 	* stdio-common/Makefile [tests]: Add new tst-fmemopen3.
>> 	* stdio-common/psiginfo.c [psiginfo]: Call __fmemopen instead of
>> 	fmemopen.
>> 	* stdio-common/tst-fmemopen3.c: New file: more fmemopen tests, focus
>> 	on append and read mode.
>> 	* sysdeps/unix/sysv/linux/aarch64/nptl/libc.abilist [GLIBC_2.20]: Add
>> 	fmemopen.
>> 	* sysdeps/unix/sysv/linux/alpha/libc.abilist [GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/arm/libc.abilist [GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/i386/libc.abilist [GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/m68k/coldfire/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/m68k/m680x0/nptl/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/microblaze/nptl/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/mips/mips32/fpu/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/mips/mips32/nofpu/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/mips/mips64/n32/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/mips/mips64/n64/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/powerpc/powerpc64/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/s390/s390-32/nptl/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/s390/s390-64/nptl/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/sh/nptl/libc.abilist [GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/sparc/sparc32/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/sparc/sparc64/nptl/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
>> 	[GLIBC_2.20]: Likewise.
>> 	* sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/x86_64/64/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>> 	* sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist [GLIBC_2.20]:
>> 	Likewise.
>>
>> ---
>>
>> diff --git a/NEWS b/NEWS
>> index a07ea66..7a0851e 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -9,19 +9,20 @@ Version 2.20
>>
>>  * The following bugs are resolved with this release:
>>
>> -  6804, 9894, 12994, 13347, 13651, 14308, 14770, 15119, 15132, 15347, 15514,
>> -  15698, 15804, 15894, 15946, 16002, 16064, 16095, 16198, 16284, 16287,
>> -  16315, 16348, 16349, 16354, 16357, 16362, 16447, 16516, 16532, 16539,
>> -  16545, 16561, 16562, 16564, 16574, 16599, 16600, 16609, 16610, 16611,
>> -  16613, 16619, 16623, 16629, 16632, 16634, 16639, 16642, 16648, 16649,
>> -  16670, 16674, 16677, 16680, 16681, 16683, 16689, 16695, 16701, 16706,
>> -  16707, 16712, 16713, 16714, 16724, 16731, 16739, 16740, 16743, 16754,
>> -  16758, 16759, 16760, 16770, 16786, 16789, 16791, 16796, 16799, 16800,
>> -  16815, 16823, 16824, 16831, 16838, 16849, 16854, 16876, 16877, 16878,
>> -  16882, 16885, 16888, 16890, 16912, 16915, 16916, 16917, 16918, 16922,
>> -  16927, 16928, 16932, 16943, 16958, 16965, 16966, 16967, 16977, 16978,
>> -  16984, 16990, 16996, 17009, 17022, 17031, 17042, 17048, 17050, 17058,
>> -  17061, 17062, 17069, 17075, 17079, 17084, 17086, 17092, 17097.
>> +  6544, 6804, 9894, 11216, 12836, 12994, 13151, 13152, 13347, 13651, 14292,
>> +  14308, 14770, 15119, 15132, 15347, 15514, 15698, 15804, 15894, 15946,
>> +  16002, 16064, 16095, 16198, 16284, 16287, 16315, 16348, 16349, 16354,
>> +  16357, 16362, 16447, 16516, 16532, 16539, 16545, 16561, 16562, 16564,
>> +  16574, 16599, 16600, 16609, 16610, 16611, 16613, 16619, 16623, 16629,
>> +  16632, 16634, 16639, 16642, 16648, 16649, 16670, 16674, 16677, 16680,
>> +  16681, 16683, 16689, 16695, 16701, 16706, 16707, 16712, 16713, 16714,
>> +  16724, 16731, 16739, 16740, 16743, 16754, 16758, 16759, 16760, 16770,
>> +  16786, 16789, 16791, 16796, 16799, 16800, 16815, 16823, 16824, 16831,
>> +  16838, 16849, 16854, 16876, 16877, 16878, 16882, 16885, 16888, 16890,
>> +  16912, 16915, 16916, 16917, 16918, 16922, 16927, 16928, 16932, 16943,
>> +  16958, 16965, 16966, 16967, 16977, 16978, 16984, 16990, 16996, 17009,
>> +  17022, 17031, 17042, 17048, 17050, 17058, 17061, 17062, 17069, 17075,
>> +  17079, 17084, 17086, 17092, 17097.
>>
>>  * Optimized strchr implementation for AArch64.  Contributed by ARM Ltd.
>>
>> diff --git a/include/stdio.h b/include/stdio.h
>> index 9f2ea31..94ba5a5 100644
>> --- a/include/stdio.h
>> +++ b/include/stdio.h
>> @@ -152,7 +152,6 @@ libc_hidden_proto (fread_unlocked)
>>  libc_hidden_proto (fwrite_unlocked)
>>  libc_hidden_proto (fgets_unlocked)
>>  libc_hidden_proto (fputs_unlocked)
>> -libc_hidden_proto (fmemopen)
>>  libc_hidden_proto (open_memstream)
>>  libc_hidden_proto (__libc_fatal)
>>  libc_hidden_proto (__vsprintf_chk)
>> @@ -180,6 +179,9 @@ gets (char *__str)
>>  }
>>  #  endif
>>
>> +extern FILE * __fmemopen (void *buf, size_t len, const char *mode);
>> +libc_hidden_proto (__fmemopen)
>> +
>>  __END_DECLS
>>  # endif
>>
>> diff --git a/libio/Makefile b/libio/Makefile
>> index 56952ce..c76b37e 100644
>> --- a/libio/Makefile
>> +++ b/libio/Makefile
>> @@ -46,7 +46,7 @@ routines	:=							      \
>>  	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
>>  	__fpurge __fpending __fsetlocking				      \
>>  									      \
>> -	libc_fatal fmemopen
>> +	libc_fatal fmemopen oldfmemopen
>>
>>  tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
>>  	tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
>> diff --git a/libio/Versions b/libio/Versions
>> index 8df89d2..7ffa64d 100644
>> --- a/libio/Versions
>> +++ b/libio/Versions
>> @@ -148,6 +148,10 @@ libc {
>>    GLIBC_2.4 {
>>      open_wmemstream;
>>    }
>> +  GLIBC_2.20 {
>> +    # f*
>> +    fmemopen;
>> +  }
>>    GLIBC_PRIVATE {
>>      # Used by NPTL and librt
>>      __libc_fatal;
>> diff --git a/libio/fmemopen.c b/libio/fmemopen.c
>> index aee2696..53e9d57 100644
>> --- a/libio/fmemopen.c
>> +++ b/libio/fmemopen.c
>> @@ -1,7 +1,6 @@
>> -/* Fmemopen implementation.
>> -   Copyright (C) 2000-2014 Free Software Foundation, Inc.
>> +/* fmemopen implementation.
>> +   Copyright (C) 2014 Free Software Foundation, Inc.
>>     This file is part of the GNU C Library.
>> -   Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
>>
>>     The GNU C Library is free software; you can redistribute it and/or
>>     modify it under the terms of the GNU Lesser General Public
>> @@ -17,54 +16,10 @@
>>     License along with the GNU C Library; if not, see
>>     <http://www.gnu.org/licenses/>.  */
>>
>> -/*
>> - * fmemopen() - "my" version of a string stream
>> - * Hanno Mueller, kontakt@hanno.de
>> - *
>> - *
>> - * I needed fmemopen() for an application that I currently work on,
>> - * but couldn't find it in libio. The following snippet of code is an
>> - * attempt to implement what glibc's documentation describes.
>> - *
>> - *
>> - *
>> - * I already see some potential problems:
>> - *
>> - * - I never used the "original" fmemopen(). I am sure that "my"
>> - *   fmemopen() behaves differently than the original version.
>> - *
>> - * - The documentation doesn't say wether a string stream allows
>> - *   seeks. I checked the old fmemopen implementation in glibc's stdio
>> - *   directory, wasn't quite able to see what is going on in that
>> - *   source, but as far as I understand there was no seek there. For
>> - *   my application, I needed fseek() and ftell(), so it's here.
>> - *
>> - * - "append" mode and fseek(p, SEEK_END) have two different ideas
>> - *   about the "end" of the stream.
>> - *
>> - *   As described in the documentation, when opening the file in
>> - *   "append" mode, the position pointer will be set to the first null
>> - *   character of the string buffer (yet the buffer may already
>> - *   contain more data). For fseek(), the last byte of the buffer is
>> - *   used as the end of the stream.
>> - *
>> - * - It is unclear to me what the documentation tries to say when it
>> - *   explains what happens when you use fmemopen with a NULL
>> - *   buffer.
>> - *
>> - *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
>> - *   is really only useful if you are going to write things to the
>> - *   buffer and then read them back in again."
>> - *
>> - *   What does that mean if the original fmemopen() did not allow
>> - *   seeking? How do you read what you just wrote without seeking back
>> - *   to the beginning of the stream?
>> - *
>> - * - I think there should be a second version of fmemopen() that does
>> - *   not add null characters for each write. (At least in my
>> - *   application, I am not actually using strings but binary data and
>> - *   so I don't need the stream to add null characters on its own.)
>> - */
>> +/* fmemopen() from 2.20 and forward works as defined by POSIX.  It also
>> +   provides an older symbol, version 2.2.5, that behaves different regarding
>> +   SEEK_END (libio/oldfmemopen.c).  */
>> +
>>
>>  #include <errno.h>
>>  #include <libio.h>
>> @@ -79,25 +34,23 @@
>>  typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
>>  struct fmemopen_cookie_struct
>>  {
>> -  char *buffer;
>> -  int mybuffer;
>> -  int binmode;
>> -  size_t size;
>> -  _IO_off64_t pos;
>> -  size_t maxpos;
>> +  char        *buffer;   /* memory buffer.  */
>> +  int         mybuffer;  /* own allocated buffer?  */
>> +  int         append;    /* buffer openened for append?  */
>> +  size_t      size;      /* buffer length in bytes.  */
>> +  _IO_off64_t pos;       /* current position at the buffer.  */
>> +  size_t      maxpos;    /* max position in buffer.  */
>>  };
>>
>>
>>  static ssize_t
>>  fmemopen_read (void *cookie, char *b, size_t s)
>>  {
>> -  fmemopen_cookie_t *c;
>> -
>> -  c = (fmemopen_cookie_t *) cookie;
>> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
>>
>> -  if (c->pos + s > c->size)
>> +  if (c->pos + s > c->maxpos)
>>      {
>> -      if ((size_t) c->pos == c->size)
>> +      if ((size_t) c->pos == c->maxpos)
>>  	return 0;
>>        s = c->size - c->pos;
>>      }
>> @@ -115,29 +68,28 @@ fmemopen_read (void *cookie, char *b, size_t s)
>>  static ssize_t
>>  fmemopen_write (void *cookie, const char *b, size_t s)
>>  {
>> -  fmemopen_cookie_t *c;
>> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;;
>> +  _IO_off64_t pos = c->append ? c->maxpos : c->pos;
>>    int addnullc;
>>
>> -  c = (fmemopen_cookie_t *) cookie;
>> -
>> -  addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
>> +  addnullc = (s == 0 || b[s - 1] != '\0');
>>
>> -  if (c->pos + s + addnullc > c->size)
>> +  if (pos + s + addnullc > c->size)
>>      {
>> -      if ((size_t) (c->pos + addnullc) == c->size)
>> +      if ((size_t) (pos + addnullc) >= c->size)
>>  	{
>>  	  __set_errno (ENOSPC);
>>  	  return 0;
>>  	}
>> -      s = c->size - c->pos - addnullc;
>> +      s = c->size - pos - addnullc;
>>      }
>>
>> -  memcpy (&(c->buffer[c->pos]), b, s);
>> +  memcpy (&(c->buffer[pos]), b, s);
>>
>> -  c->pos += s;
>> -  if ((size_t) c->pos > c->maxpos)
>> +  pos += s;
>> +  if ((size_t) pos > c->maxpos)
>>      {
>> -      c->maxpos = c->pos;
>> +      c->maxpos = pos;
>>        if (addnullc)
>>  	c->buffer[c->maxpos] = '\0';
>>      }
>> @@ -150,9 +102,7 @@ static int
>>  fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>>  {
>>    _IO_off64_t np;
>> -  fmemopen_cookie_t *c;
>> -
>> -  c = (fmemopen_cookie_t *) cookie;
>> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
>>
>>    switch (w)
>>      {
>> @@ -165,7 +115,7 @@ fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>>        break;
>>
>>      case SEEK_END:
>> -      np = (c->binmode ? c->size : c->maxpos) - *p;
>> +      np = c->maxpos + *p;
>>        break;
>>
>>      default:
>> @@ -184,9 +134,7 @@ fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>>  static int
>>  fmemopen_close (void *cookie)
>>  {
>> -  fmemopen_cookie_t *c;
>> -
>> -  c = (fmemopen_cookie_t *) cookie;
>> +  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
>>
>>    if (c->mybuffer)
>>      free (c->buffer);
>> @@ -197,18 +145,11 @@ fmemopen_close (void *cookie)
>>
>>
>>  FILE *
>> -fmemopen (void *buf, size_t len, const char *mode)
>> +__fmemopen (void *buf, size_t len, const char *mode)
>>  {
>>    cookie_io_functions_t iof;
>>    fmemopen_cookie_t *c;
>>
>> -  if (__glibc_unlikely (len == 0))
>> -    {
>> -    einval:
>> -      __set_errno (EINVAL);
>> -      return NULL;
>> -    }
>> -
>>    c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
>>    if (c == NULL)
>>      return NULL;
>> @@ -231,26 +172,38 @@ fmemopen (void *buf, size_t len, const char *mode)
>>        if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
>>  	{
>>  	  free (c);
>> -	  goto einval;
>> +	  __set_errno (EINVAL);
>> +	  return NULL;
>>  	}
>>
>>        c->buffer = buf;
>>
>> -      if (mode[0] == 'w')
>> +      /* POSIX states that w+ mode should truncate the buffer.  */
>> +      if (mode[0] == 'w' && mode[1] == '+')
>>  	c->buffer[0] = '\0';
>>
>>        c->maxpos = strnlen (c->buffer, len);
>>      }
>>
>> +
>> +  /* Mode   |  starting position (cookie::pos) |          size (cookie::size)
>> +   * ------ |----------------------------------|-----------------------------
>> +   * read   |          beginning of the buffer |                size argument
>> +   * write  |          beginning of the buffer |                         zero
>> +   * append |    first null or size buffer + 1 |  first null or size argument
>> +   */
>> +
>>    c->size = len;
>>
>> -  if (mode[0] == 'a')
>> +  if (mode[0] == 'r')
>> +    c->maxpos = len;
>> +
>> +  c->append = mode[0] == 'a';
>> +  if (c->append)
>>      c->pos = c->maxpos;
>>    else
>>      c->pos = 0;
>>
>> -  c->binmode = mode[0] != '\0' && mode[1] == 'b';
>> -
>>    iof.read = fmemopen_read;
>>    iof.write = fmemopen_write;
>>    iof.seek = fmemopen_seek;
>> @@ -258,4 +211,5 @@ fmemopen (void *buf, size_t len, const char *mode)
>>
>>    return _IO_fopencookie (c, mode, iof);
>>  }
>> -libc_hidden_def (fmemopen)
>> +libc_hidden_def (__fmemopen)
>> +versioned_symbol (libc, __fmemopen, fmemopen, GLIBC_2_20);
>> diff --git a/libio/oldfmemopen.c b/libio/oldfmemopen.c
>> new file mode 100644
>> index 0000000..4e85ebe
>> --- /dev/null
>> +++ b/libio/oldfmemopen.c
>> @@ -0,0 +1,265 @@
>> +/* Fmemopen implementation.
>> +   Copyright (C) 2000-2014 Free Software Foundation, Inc.
>> +   This file is part of the GNU C Library.
>> +   Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
>> +
>> +   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
>> +   <http://www.gnu.org/licenses/>.  */
>> +
>> +/*
>> + * fmemopen() - "my" version of a string stream
>> + * Hanno Mueller, kontakt@hanno.de
>> + *
>> + *
>> + * I needed fmemopen() for an application that I currently work on,
>> + * but couldn't find it in libio. The following snippet of code is an
>> + * attempt to implement what glibc's documentation describes.
>> + *
>> + *
>> + *
>> + * I already see some potential problems:
>> + *
>> + * - I never used the "original" fmemopen(). I am sure that "my"
>> + *   fmemopen() behaves differently than the original version.
>> + *
>> + * - The documentation doesn't say wether a string stream allows
>> + *   seeks. I checked the old fmemopen implementation in glibc's stdio
>> + *   directory, wasn't quite able to see what is going on in that
>> + *   source, but as far as I understand there was no seek there. For
>> + *   my application, I needed fseek() and ftell(), so it's here.
>> + *
>> + * - "append" mode and fseek(p, SEEK_END) have two different ideas
>> + *   about the "end" of the stream.
>> + *
>> + *   As described in the documentation, when opening the file in
>> + *   "append" mode, the position pointer will be set to the first null
>> + *   character of the string buffer (yet the buffer may already
>> + *   contain more data). For fseek(), the last byte of the buffer is
>> + *   used as the end of the stream.
>> + *
>> + * - It is unclear to me what the documentation tries to say when it
>> + *   explains what happens when you use fmemopen with a NULL
>> + *   buffer.
>> + *
>> + *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
>> + *   is really only useful if you are going to write things to the
>> + *   buffer and then read them back in again."
>> + *
>> + *   What does that mean if the original fmemopen() did not allow
>> + *   seeking? How do you read what you just wrote without seeking back
>> + *   to the beginning of the stream?
>> + *
>> + * - I think there should be a second version of fmemopen() that does
>> + *   not add null characters for each write. (At least in my
>> + *   application, I am not actually using strings but binary data and
>> + *   so I don't need the stream to add null characters on its own.)
>> + */
>> +
>> +#include "libioP.h"
>> +
>> +#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_20)
>> +
>> +#include <errno.h>
>> +#include <libio.h>
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <stdint.h>
>> +#include <string.h>
>> +#include <sys/types.h>
>> +
>> +
>> +typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
>> +struct fmemopen_cookie_struct
>> +{
>> +  char *buffer;
>> +  int mybuffer;
>> +  int binmode;
>> +  size_t size;
>> +  _IO_off64_t pos;
>> +  size_t maxpos;
>> +};
>> +
>> +
>> +static ssize_t
>> +fmemopen_read (void *cookie, char *b, size_t s)
>> +{
>> +  fmemopen_cookie_t *c;
>> +
>> +  c = (fmemopen_cookie_t *) cookie;
>> +
>> +  if (c->pos + s > c->size)
>> +    {
>> +      if ((size_t) c->pos == c->size)
>> +	return 0;
>> +      s = c->size - c->pos;
>> +    }
>> +
>> +  memcpy (b, &(c->buffer[c->pos]), s);
>> +
>> +  c->pos += s;
>> +  if ((size_t) c->pos > c->maxpos)
>> +    c->maxpos = c->pos;
>> +
>> +  return s;
>> +}
>> +
>> +
>> +static ssize_t
>> +fmemopen_write (void *cookie, const char *b, size_t s)
>> +{
>> +  fmemopen_cookie_t *c;
>> +  int addnullc;
>> +
>> +  c = (fmemopen_cookie_t *) cookie;
>> +
>> +  addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
>> +
>> +  if (c->pos + s + addnullc > c->size)
>> +    {
>> +      if ((size_t) (c->pos + addnullc) == c->size)
>> +	{
>> +	  __set_errno (ENOSPC);
>> +	  return 0;
>> +	}
>> +      s = c->size - c->pos - addnullc;
>> +    }
>> +
>> +  memcpy (&(c->buffer[c->pos]), b, s);
>> +
>> +  c->pos += s;
>> +  if ((size_t) c->pos > c->maxpos)
>> +    {
>> +      c->maxpos = c->pos;
>> +      if (addnullc)
>> +	c->buffer[c->maxpos] = '\0';
>> +    }
>> +
>> +  return s;
>> +}
>> +
>> +
>> +static int
>> +fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
>> +{
>> +  _IO_off64_t np;
>> +  fmemopen_cookie_t *c;
>> +
>> +  c = (fmemopen_cookie_t *) cookie;
>> +
>> +  switch (w)
>> +    {
>> +    case SEEK_SET:
>> +      np = *p;
>> +      break;
>> +
>> +    case SEEK_CUR:
>> +      np = c->pos + *p;
>> +      break;
>> +
>> +    case SEEK_END:
>> +      np = (c->binmode ? c->size : c->maxpos) - *p;
>> +      break;
>> +
>> +    default:
>> +      return -1;
>> +    }
>> +
>> +  if (np < 0 || (size_t) np > c->size)
>> +    return -1;
>> +
>> +  *p = c->pos = np;
>> +
>> +  return 0;
>> +}
>> +
>> +
>> +static int
>> +fmemopen_close (void *cookie)
>> +{
>> +  fmemopen_cookie_t *c;
>> +
>> +  c = (fmemopen_cookie_t *) cookie;
>> +
>> +  if (c->mybuffer)
>> +    free (c->buffer);
>> +  free (c);
>> +
>> +  return 0;
>> +}
>> +
>> +
>> +FILE *
>> +__old_fmemopen (void *buf, size_t len, const char *mode)
>> +{
>> +  cookie_io_functions_t iof;
>> +  fmemopen_cookie_t *c;
>> +
>> +  if (__glibc_unlikely (len == 0))
>> +    {
>> +    einval:
>> +      __set_errno (EINVAL);
>> +      return NULL;
>> +    }
>> +
>> +  c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
>> +  if (c == NULL)
>> +    return NULL;
>> +
>> +  c->mybuffer = (buf == NULL);
>> +
>> +  if (c->mybuffer)
>> +    {
>> +      c->buffer = (char *) malloc (len);
>> +      if (c->buffer == NULL)
>> +	{
>> +	  free (c);
>> +	  return NULL;
>> +	}
>> +      c->buffer[0] = '\0';
>> +      c->maxpos = 0;
>> +    }
>> +  else
>> +    {
>> +      if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
>> +	{
>> +	  free (c);
>> +	  goto einval;
>> +	}
>> +
>> +      c->buffer = buf;
>> +
>> +      if (mode[0] == 'w')
>> +	c->buffer[0] = '\0';
>> +
>> +      c->maxpos = strnlen (c->buffer, len);
>> +    }
>> +
>> +  c->size = len;
>> +
>> +  if (mode[0] == 'a')
>> +    c->pos = c->maxpos;
>> +  else
>> +    c->pos = 0;
>> +
>> +  c->binmode = mode[0] != '\0' && mode[1] == 'b';
>> +
>> +  iof.read = fmemopen_read;
>> +  iof.write = fmemopen_write;
>> +  iof.seek = fmemopen_seek;
>> +  iof.close = fmemopen_close;
>> +
>> +  return _IO_fopencookie (c, mode, iof);
>> +}
>> +compat_symbol (libc, __old_fmemopen, fmemopen, GLIBC_2_2);
>> +#endif
>> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
>> index 5f8e534..5971e65 100644
>> --- a/stdio-common/Makefile
>> +++ b/stdio-common/Makefile
>> @@ -57,7 +57,7 @@ tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
>>  	 bug19 bug19a tst-popen2 scanf13 scanf14 scanf15 bug20 bug21 bug22 \
>>  	 scanf16 scanf17 tst-setvbuf1 tst-grouping bug23 bug24 \
>>  	 bug-vfprintf-nargs tst-long-dbl-fphex tst-fphex-wide tst-sprintf3 \
>> -	 bug25 tst-printf-round bug26
>> +	 bug25 tst-printf-round bug26 tst-fmemopen3
>>
>>  test-srcs = tst-unbputc tst-printf
>>
>> diff --git a/stdio-common/psiginfo.c b/stdio-common/psiginfo.c
>> index 564d237..62a9671 100644
>> --- a/stdio-common/psiginfo.c
>> +++ b/stdio-common/psiginfo.c
>> @@ -60,7 +60,7 @@ void
>>  psiginfo (const siginfo_t *pinfo, const char *s)
>>  {
>>    char buf[512];
>> -  FILE *fp = fmemopen (buf, sizeof (buf), "w");
>> +  FILE *fp = __fmemopen (buf, sizeof (buf), "w");
>>    if (fp == NULL)
>>      {
>>        const char *colon;
>> diff --git a/stdio-common/tst-fmemopen3.c b/stdio-common/tst-fmemopen3.c
>> new file mode 100644
>> index 0000000..1ff0920
>> --- /dev/null
>> +++ b/stdio-common/tst-fmemopen3.c
>> @@ -0,0 +1,206 @@
>> +/* fmemopen tests for append and read mode.
>> +   Copyright (C) 2014 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
>> +   <http://www.gnu.org/licenses/>.  */
>> +
>> +#include <assert.h>
>> +#include <stdio.h>
>> +#include <string.h>
>> +#include <sys/types.h>
>> +
>> +static void
>> +print_buffer (const char *s, size_t n)
>> +{
>> +  size_t i;
>> +  for (i=0; i<n; ++i)
>> +    printf ("0x%02X (%c), ", s[i], s[i]);
>> +}
>> +
>> +/* This test check append mode initial position (a/a+) based on POSIX defition
>> +   (BZ#6544 and BZ#13151).  */
>> +static int
>> +do_test_write_append (const char *mode)
>> +{
>> +  char buf[32] = "testing buffer";
>> +  char exp[32] = "testing bufferXX";
>> +
>> +  FILE *fp = fmemopen (buf, sizeof (buf), mode);
>> +
>> +  fflush (fp);
>> +  fprintf (fp, "X");
>> +  fseek (fp, 0, SEEK_SET);
>> +  fprintf (fp, "X");
>> +  fclose (fp);
>> +
>> +  if (strcmp (buf, exp) != 0)
>> +    {
>> +      printf ("%s: check failed: %s != %s\n", __FUNCTION__, buf, exp);
>> +      return 1;
>> +    }
>> +
>> +  return 0;
>> +}
>> +
>> +/* This test check append mode initial position (a/a+) based on POSIX defition
>> +   (BZ#6544 and BZ#13151) for buffer without null byte end.  */
>> +static int
>> +do_test_write_append_without_null (const char *mode)
>> +{
>> +  char buf[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
>> +  char exp[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
>> +
>> +  /* If '\0' is not found in buffer, POSIX states that SEEK_SET should be
>> +     the size argument.  */
>> +  FILE *fp = fmemopen (buf, sizeof (buf) - 2, "a");
>> +
>> +  fflush (fp);
>> +  fputc (0x70, fp);
>> +  fseek (fp, 0, SEEK_SET);
>> +  fputc (0x70, fp);
>> +  fputc (0x70, fp);
>> +  fclose (fp);
>> +
>> +  /* POSIX also states that a write operation on the stream shall not advance
>> +     the current buffer size beyond the size given in fmemopen, so the string
>> +     should be same.  */
>> +  if (memcmp (buf, exp, sizeof (buf)) != 0)
>> +    {
>> +      printf ("%s: check failed: ", __FUNCTION__);
>> +      print_buffer (buf, sizeof (buf));
>> +      printf ("!= ");
>> +      print_buffer (exp, sizeof (exp));
>> +      printf ("\n");
>> +      return 1;
>> +    }
>> +
>> +  return 0;
>> +}
>> +
>> +/* This test check for initial position and feek value for fmemopen objects
>> +   opened with append mode.  */
>> +static int
>> +do_test_read_append (void)
>> +{
>> +  char buf[32] = "testing buffer";
>> +  size_t buflen = strlen (buf);
>> +  long fpos;
>> +
>> +  /* POSIX defines for 'a+' the initial position is the first null byte.  */
>> +  FILE *fp = fmemopen (buf, sizeof (buf), "a+");
>> +
>> +  fpos = ftell (fp);
>> +  if (fpos != buflen)
>> +    {
>> +      printf ("%s: ftell|SEEK_SET (fp) %li != strlen (%s) %li\n",
>> +	      __FUNCTION__, fpos, buf, buflen);
>> +      fclose (fp);
>> +      return 1;
>> +    }
>> +
>> +  fseek (fp, 0, SEEK_END);
>> +
>> +  if (fpos != buflen)
>> +    {
>> +      printf ("%s: ftell|SEEK_END (fp) %li != strlen (%s) %li\n",
>> +	      __FUNCTION__, fpos, buf, buflen);
>> +      fclose (fp);
>> +      return 1;
>> +    }
>> +  fclose (fp);
>> +
>> +  /* Check if attempting to read past the current size, defined as strlen (buf)
>> +     yield an EOF.  */
>> +  fp = fmemopen (buf, sizeof (buf), "a+");
>> +  if (getc(fp) != EOF)
>> +    {
>> +      printf ("%s: getc(fp) != EOF\n", __FUNCTION__);
>> +      fclose (fp);
>> +      return -1;
>> +    }
>> +
>> +  fclose (fp);
>> +
>> +  return 0;
>> +}
>> +
>> +/* This test check for fseek (SEEK_END) using negative offsets (BZ#14292).  The
>> +   starting position of descriptor is different base on the opening mode.  */
>> +static int
>> +do_test_read_seek_neg (const char *mode, const char *expected)
>> +{
>> +  char buf[] = "abcdefghijklmnopqrstuvxz0123456789";
>> +  char tmp[10];
>> +  size_t tmps = sizeof (tmps);
>> +  long offset = -11;
>> +
>> +  FILE *fp = fmemopen (buf, sizeof (buf), mode);
>> +  fseek (fp, offset, SEEK_END);
>> +  fread (tmp, tmps, 1, fp);
>> +  
>> +  if (memcmp (tmp, expected, tmps) != 0)
>> +    {
>> +      printf ("%s: fmemopen(%s) - fseek (fp, %li, SEEK_END):\n",
>> +	      __FUNCTION__, mode, offset);
>> +      printf ("  returned: ");
>> +      print_buffer (tmp, tmps);
>> +      printf ("\n");
>> +      printf ("  expected: ");
>> +      print_buffer (expected, tmps);
>> +      printf ("\n");
>> +      return 1;
>> +    }
>> +  
>> +  fclose (fp);
>> +
>> +  return 0;
>> +}
>> +
>> +static int
>> +do_test_read_seek_negative (void)
>> +{
>> +  int ret = 0;
>> +
>> +  /* 'r' and 'w' modes defines the initial position at the buffer start and
>> +     seek with SEEK_END shall seek relative to its size give in fmemopen
>> +     call.  The expected tmp result is 0 to 9 *without* the ending null  */
>> +  ret += do_test_read_seek_neg ("r", "0123456789");
>> +  /* 'a+' mode sets the initial position at the first null byte in buffer and
>> +    SEEK_END shall seek relative to its size as well.  The expected result is
>> +    z012345678, since SEEK_END plus a+ start at '\0', not size.  */
>> +  ret += do_test_read_seek_neg ("a+", "z012345678");
>> +
>> +  return ret;
>> +}
>> +
>> +static int
>> +do_test (void)
>> +{
>> +  int ret = 0;
>> +
>> +  ret += do_test_write_append ("a");
>> +  ret += do_test_write_append_without_null ("a");
>> +  ret += do_test_write_append ("a+");
>> +  ret += do_test_write_append_without_null ("a+");
>> +
>> +  ret += do_test_read_append ();
>> +
>> +  ret += do_test_read_seek_negative ();
>> +
>> +  return ret;
>> +}
>> +
>> +#define TEST_FUNCTION do_test ()
>> +#include "../test-skeleton.c"
>> diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
>> index aeee312..6cf2ddd 100644
>> --- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
>> @@ -2081,3 +2081,6 @@ GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>>   _mcount F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>> diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
>> index 980e088..11f03a5 100644
>> --- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
>> @@ -1822,6 +1822,9 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.2
>>   GLIBC_2.2 A
>>   _IO_adjust_wcolumn F
>> diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
>> index ce45208..0a6ef89 100644
>> --- a/sysdeps/unix/sysv/linux/arm/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
>> @@ -89,6 +89,9 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.4
>>   GLIBC_2.4 A
>>   _Exit F
>> diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
>> index 3cb314d..af60934 100644
>> --- a/sysdeps/unix/sysv/linux/i386/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
>> @@ -2023,6 +2023,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist b/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
>> index 067552d..afe6f4b 100644
>> --- a/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
>> @@ -1881,6 +1881,9 @@ GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>>   getunwind F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
>> index f06cc8e..2bc1ce4 100644
>> --- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
>> @@ -90,6 +90,9 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.4
>>   GLIBC_2.4 A
>>   _Exit F
>> diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
>> index 9010ea7..48c25b4 100644
>> --- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
>> @@ -1979,6 +1979,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
>> index 6e8d993..8b506ed 100644
>> --- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
>> @@ -2080,3 +2080,6 @@ GLIBC_2.18
>>   xencrypt F
>>   xprt_register F
>>   xprt_unregister F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
>> index 1c3490c..eb7860e 100644
>> --- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
>> @@ -1951,6 +1951,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
>> index d8fd823..e4385d2 100644
>> --- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
>> @@ -1949,6 +1949,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
>> index 3e6ed35..44b4f20 100644
>> --- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
>> @@ -1947,6 +1947,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
>> index c7e46aa..2c00ad7 100644
>> --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
>> @@ -1941,6 +1941,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
>> index f27b48b..29b9695 100644
>> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
>> @@ -1983,6 +1983,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
>> index a54382e..b57f378 100644
>> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
>> @@ -1989,6 +1989,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
>> index 195b587..7a1deff 100644
>> --- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
>> @@ -90,6 +90,9 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   _Exit F
>> diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
>> index 03f2e83..d23377d 100644
>> --- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
>> @@ -1985,6 +1985,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
>> index 4576fc8..7a4081e 100644
>> --- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
>> @@ -1881,6 +1881,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
>> index a653292..51f8707 100644
>> --- a/sysdeps/unix/sysv/linux/sh/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
>> @@ -1864,6 +1864,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
>> index 9defbdf..46490a7 100644
>> --- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
>> @@ -1975,6 +1975,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
>> index 35987fa..540bc1c 100644
>> --- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
>> @@ -1908,6 +1908,9 @@ GLIBC_2.2.4
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
>> index caf74b8..a473437 100644
>> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
>> @@ -2091,3 +2091,6 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>> diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
>> index 68d975b..16bd028 100644
>> --- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
>> @@ -2091,3 +2091,6 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>> diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
>> index caf74b8..a473437 100644
>> --- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
>> @@ -2091,3 +2091,6 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>> diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
>> index 914b590..b77feba 100644
>> --- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
>> @@ -1854,6 +1854,9 @@ GLIBC_2.2.5
>>  GLIBC_2.2.6
>>   GLIBC_2.2.6 A
>>   __nanosleep F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>  GLIBC_2.3
>>   GLIBC_2.3 A
>>   __ctype_b_loc F
>> diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
>> index 0f64c8d..f638884 100644
>> --- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
>> +++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
>> @@ -2089,3 +2089,6 @@ GLIBC_2.17
>>  GLIBC_2.18
>>   GLIBC_2.18 A
>>   __cxa_thread_atexit_impl F
>> +GLIBC_2.20
>> + GLIBC_2.20 A
>> + fmemopen F
>>
>>
>
  
Joseph Myers July 16, 2014, 9:20 p.m. UTC | #3
This patch looks like it needs updating for hppa now having its own 
libc.abilist.
  
Adhemerval Zanella Netto July 17, 2014, 6:49 p.m. UTC | #4
On 16-07-2014 18:20, Joseph S. Myers wrote:
> This patch looks like it needs updating for hppa now having its own 
> libc.abilist.
>
Thanks, I though I have covered all abi files. What about the implementation itself?
  
Adhemerval Zanella Netto July 22, 2014, 12:59 p.m. UTC | #5
On 17-07-2014 15:49, Adhemerval Zanella wrote:
> On 16-07-2014 18:20, Joseph S. Myers wrote:
>> This patch looks like it needs updating for hppa now having its own 
>> libc.abilist.
>>
> Thanks, I though I have covered all abi files. What about the implementation itself?
>
Ping about patch contents.
  
Adhemerval Zanella Netto July 28, 2014, 12:05 p.m. UTC | #6
On 22-07-2014 09:59, Adhemerval Zanella wrote:
> On 17-07-2014 15:49, Adhemerval Zanella wrote:
>> On 16-07-2014 18:20, Joseph S. Myers wrote:
>>> This patch looks like it needs updating for hppa now having its own 
>>> libc.abilist.
>>>
>> Thanks, I though I have covered all abi files. What about the implementation itself?
>>
> Ping about patch contents.
>
Ping.  I have update my branch azanella/fmemopen.
  
Carlos O'Donell July 30, 2014, 3:56 a.m. UTC | #7
On 07/28/2014 08:05 AM, Adhemerval Zanella wrote:
> On 22-07-2014 09:59, Adhemerval Zanella wrote:
>> On 17-07-2014 15:49, Adhemerval Zanella wrote:
>>> On 16-07-2014 18:20, Joseph S. Myers wrote:
>>>> This patch looks like it needs updating for hppa now having its own 
>>>> libc.abilist.
>>>>
>>> Thanks, I though I have covered all abi files. What about the implementation itself?
>>>
>> Ping about patch contents.
>>
> Ping.  I have update my branch azanella/fmemopen.
> 

I started reviewing this and IMO it's going to take
more time than we have during the freeze.

I see no strong or compelling argument to put it
into 2.20.

If it went into 2.21 we could test it in rawhide and
make sure nothing else was broken.

If I've learned anything from Siddhesh's changes
in the libio code it's that nothing is as easy as
you think and distribution testing is critical
to finding issues.

My strong recommendation is that this get deferred
until 2.21.

Unless you have done distribution testing already?

Cheers,
Carlos.
  
Adhemerval Zanella Netto July 30, 2014, 11:52 a.m. UTC | #8
On 30-07-2014 00:56, Carlos O'Donell wrote:
> On 07/28/2014 08:05 AM, Adhemerval Zanella wrote:
>> On 22-07-2014 09:59, Adhemerval Zanella wrote:
>>> On 17-07-2014 15:49, Adhemerval Zanella wrote:
>>>> On 16-07-2014 18:20, Joseph S. Myers wrote:
>>>>> This patch looks like it needs updating for hppa now having its own 
>>>>> libc.abilist.
>>>>>
>>>> Thanks, I though I have covered all abi files. What about the implementation itself?
>>>>
>>> Ping about patch contents.
>>>
>> Ping.  I have update my branch azanella/fmemopen.
>>
> I started reviewing this and IMO it's going to take
> more time than we have during the freeze.
>
> I see no strong or compelling argument to put it
> into 2.20.
>
> If it went into 2.21 we could test it in rawhide and
> make sure nothing else was broken.
>
> If I've learned anything from Siddhesh's changes
> in the libio code it's that nothing is as easy as
> you think and distribution testing is critical
> to finding issues.
>
> My strong recommendation is that this get deferred
> until 2.21.
>
> Unless you have done distribution testing already?

I have not and it is no problem to move it to 2.21.  I just a pity we 
can't add it on 2.20...

>
> Cheers,
> Carlos.
>
  
Adhemerval Zanella Netto Sept. 10, 2014, 2:13 p.m. UTC | #9
On 30-07-2014 08:52, Adhemerval Zanella wrote:
> On 30-07-2014 00:56, Carlos O'Donell wrote:
>> On 07/28/2014 08:05 AM, Adhemerval Zanella wrote:
>>> On 22-07-2014 09:59, Adhemerval Zanella wrote:
>>>> On 17-07-2014 15:49, Adhemerval Zanella wrote:
>>>>> On 16-07-2014 18:20, Joseph S. Myers wrote:
>>>>>> This patch looks like it needs updating for hppa now having its own 
>>>>>> libc.abilist.
>>>>>>
>>>>> Thanks, I though I have covered all abi files. What about the implementation itself?
>>>>>
>>>> Ping about patch contents.
>>>>
>>> Ping.  I have update my branch azanella/fmemopen.
>>>
>> I started reviewing this and IMO it's going to take
>> more time than we have during the freeze.
>>
>> I see no strong or compelling argument to put it
>> into 2.20.
>>
>> If it went into 2.21 we could test it in rawhide and
>> make sure nothing else was broken.
>>
>> If I've learned anything from Siddhesh's changes
>> in the libio code it's that nothing is as easy as
>> you think and distribution testing is critical
>> to finding issues.
>>
>> My strong recommendation is that this get deferred
>> until 2.21.
>>
>> Unless you have done distribution testing already?
> I have not and it is no problem to move it to 2.21.  I just a pity we 
> can't add it on 2.20...
>
>> Cheers,
>> Carlos.
>>
I have update the symbol versioning and abilist for 2.21 and pushed it to
branch [1].


[1] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/fmemopen
  
Adhemerval Zanella Netto Sept. 22, 2014, 3 p.m. UTC | #10
On 10-09-2014 11:13, Adhemerval Zanella wrote:
> On 30-07-2014 08:52, Adhemerval Zanella wrote:
>> On 30-07-2014 00:56, Carlos O'Donell wrote:
>>> On 07/28/2014 08:05 AM, Adhemerval Zanella wrote:
>>>> On 22-07-2014 09:59, Adhemerval Zanella wrote:
>>>>> On 17-07-2014 15:49, Adhemerval Zanella wrote:
>>>>>> On 16-07-2014 18:20, Joseph S. Myers wrote:
>>>>>>> This patch looks like it needs updating for hppa now having its own 
>>>>>>> libc.abilist.
>>>>>>>
>>>>>> Thanks, I though I have covered all abi files. What about the implementation itself?
>>>>>>
>>>>> Ping about patch contents.
>>>>>
>>>> Ping.  I have update my branch azanella/fmemopen.
>>>>
>>> I started reviewing this and IMO it's going to take
>>> more time than we have during the freeze.
>>>
>>> I see no strong or compelling argument to put it
>>> into 2.20.
>>>
>>> If it went into 2.21 we could test it in rawhide and
>>> make sure nothing else was broken.
>>>
>>> If I've learned anything from Siddhesh's changes
>>> in the libio code it's that nothing is as easy as
>>> you think and distribution testing is critical
>>> to finding issues.
>>>
>>> My strong recommendation is that this get deferred
>>> until 2.21.
>>>
>>> Unless you have done distribution testing already?
>> I have not and it is no problem to move it to 2.21.  I just a pity we 
>> can't add it on 2.20...
>>
>>> Cheers,
>>> Carlos.
>>>
> I have update the symbol versioning and abilist for 2.21 and pushed it to
> branch [1].
>
>
> [1] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/fmemopen 
>
Ping.
  
Adhemerval Zanella Netto Oct. 7, 2014, 12:43 p.m. UTC | #11
On 22-09-2014 12:00, Adhemerval Zanella wrote:
> On 10-09-2014 11:13, Adhemerval Zanella wrote:
>> On 30-07-2014 08:52, Adhemerval Zanella wrote:
>>> On 30-07-2014 00:56, Carlos O'Donell wrote:
>>>> On 07/28/2014 08:05 AM, Adhemerval Zanella wrote:
>>>>> On 22-07-2014 09:59, Adhemerval Zanella wrote:
>>>>>> On 17-07-2014 15:49, Adhemerval Zanella wrote:
>>>>>>> On 16-07-2014 18:20, Joseph S. Myers wrote:
>>>>>>>> This patch looks like it needs updating for hppa now having its own 
>>>>>>>> libc.abilist.
>>>>>>>>
>>>>>>> Thanks, I though I have covered all abi files. What about the implementation itself?
>>>>>>>
>>>>>> Ping about patch contents.
>>>>>>
>>>>> Ping.  I have update my branch azanella/fmemopen.
>>>>>
>>>> I started reviewing this and IMO it's going to take
>>>> more time than we have during the freeze.
>>>>
>>>> I see no strong or compelling argument to put it
>>>> into 2.20.
>>>>
>>>> If it went into 2.21 we could test it in rawhide and
>>>> make sure nothing else was broken.
>>>>
>>>> If I've learned anything from Siddhesh's changes
>>>> in the libio code it's that nothing is as easy as
>>>> you think and distribution testing is critical
>>>> to finding issues.
>>>>
>>>> My strong recommendation is that this get deferred
>>>> until 2.21.
>>>>
>>>> Unless you have done distribution testing already?
>>> I have not and it is no problem to move it to 2.21.  I just a pity we 
>>> can't add it on 2.20...
>>>
>>>> Cheers,
>>>> Carlos.
>>>>
>> I have update the symbol versioning and abilist for 2.21 and pushed it to
>> branch [1].
>>
>>
>> [1] https://sourceware.org/git/?p=glibc.git;a=shortlog;h=refs/heads/azanella/fmemopen 
>>
> Ping.
>
Ping.
  
Rich Felker Oct. 20, 2014, 11 p.m. UTC | #12
On Wed, Jul 02, 2014 at 09:29:36AM -0300, Adhemerval Zanella wrote:
> Hi,
> 
> This is a small update on the previous fmemopen patch I have sent [1].
> the change is basically:
> 
> * 'w' mode does not truncate the buffer, only 'w+'
> 
> I also rebased against master to adjust NEWS file.

Are you aware of the issues raised here?

http://www.austingroupbugs.net/view.php?id=657

While you're changing the behavior, it would be nice to resolve this
issue too. POSIX currently specifies some nonsense with regard to how
null termination happens for "w" mode, and it doesn't match the
current glibc behavior.

Rich
  

Patch

diff --git a/NEWS b/NEWS
index a07ea66..7a0851e 100644
--- a/NEWS
+++ b/NEWS
@@ -9,19 +9,20 @@  Version 2.20
 
 * The following bugs are resolved with this release:
 
-  6804, 9894, 12994, 13347, 13651, 14308, 14770, 15119, 15132, 15347, 15514,
-  15698, 15804, 15894, 15946, 16002, 16064, 16095, 16198, 16284, 16287,
-  16315, 16348, 16349, 16354, 16357, 16362, 16447, 16516, 16532, 16539,
-  16545, 16561, 16562, 16564, 16574, 16599, 16600, 16609, 16610, 16611,
-  16613, 16619, 16623, 16629, 16632, 16634, 16639, 16642, 16648, 16649,
-  16670, 16674, 16677, 16680, 16681, 16683, 16689, 16695, 16701, 16706,
-  16707, 16712, 16713, 16714, 16724, 16731, 16739, 16740, 16743, 16754,
-  16758, 16759, 16760, 16770, 16786, 16789, 16791, 16796, 16799, 16800,
-  16815, 16823, 16824, 16831, 16838, 16849, 16854, 16876, 16877, 16878,
-  16882, 16885, 16888, 16890, 16912, 16915, 16916, 16917, 16918, 16922,
-  16927, 16928, 16932, 16943, 16958, 16965, 16966, 16967, 16977, 16978,
-  16984, 16990, 16996, 17009, 17022, 17031, 17042, 17048, 17050, 17058,
-  17061, 17062, 17069, 17075, 17079, 17084, 17086, 17092, 17097.
+  6544, 6804, 9894, 11216, 12836, 12994, 13151, 13152, 13347, 13651, 14292,
+  14308, 14770, 15119, 15132, 15347, 15514, 15698, 15804, 15894, 15946,
+  16002, 16064, 16095, 16198, 16284, 16287, 16315, 16348, 16349, 16354,
+  16357, 16362, 16447, 16516, 16532, 16539, 16545, 16561, 16562, 16564,
+  16574, 16599, 16600, 16609, 16610, 16611, 16613, 16619, 16623, 16629,
+  16632, 16634, 16639, 16642, 16648, 16649, 16670, 16674, 16677, 16680,
+  16681, 16683, 16689, 16695, 16701, 16706, 16707, 16712, 16713, 16714,
+  16724, 16731, 16739, 16740, 16743, 16754, 16758, 16759, 16760, 16770,
+  16786, 16789, 16791, 16796, 16799, 16800, 16815, 16823, 16824, 16831,
+  16838, 16849, 16854, 16876, 16877, 16878, 16882, 16885, 16888, 16890,
+  16912, 16915, 16916, 16917, 16918, 16922, 16927, 16928, 16932, 16943,
+  16958, 16965, 16966, 16967, 16977, 16978, 16984, 16990, 16996, 17009,
+  17022, 17031, 17042, 17048, 17050, 17058, 17061, 17062, 17069, 17075,
+  17079, 17084, 17086, 17092, 17097.
 
 * Optimized strchr implementation for AArch64.  Contributed by ARM Ltd.
 
diff --git a/include/stdio.h b/include/stdio.h
index 9f2ea31..94ba5a5 100644
--- a/include/stdio.h
+++ b/include/stdio.h
@@ -152,7 +152,6 @@  libc_hidden_proto (fread_unlocked)
 libc_hidden_proto (fwrite_unlocked)
 libc_hidden_proto (fgets_unlocked)
 libc_hidden_proto (fputs_unlocked)
-libc_hidden_proto (fmemopen)
 libc_hidden_proto (open_memstream)
 libc_hidden_proto (__libc_fatal)
 libc_hidden_proto (__vsprintf_chk)
@@ -180,6 +179,9 @@  gets (char *__str)
 }
 #  endif
 
+extern FILE * __fmemopen (void *buf, size_t len, const char *mode);
+libc_hidden_proto (__fmemopen)
+
 __END_DECLS
 # endif
 
diff --git a/libio/Makefile b/libio/Makefile
index 56952ce..c76b37e 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -46,7 +46,7 @@  routines	:=							      \
 	__fbufsize __freading __fwriting __freadable __fwritable __flbf	      \
 	__fpurge __fpending __fsetlocking				      \
 									      \
-	libc_fatal fmemopen
+	libc_fatal fmemopen oldfmemopen
 
 tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst_wprintf2 tst-widetext test-fmemopen tst-ext tst-ext2 \
diff --git a/libio/Versions b/libio/Versions
index 8df89d2..7ffa64d 100644
--- a/libio/Versions
+++ b/libio/Versions
@@ -148,6 +148,10 @@  libc {
   GLIBC_2.4 {
     open_wmemstream;
   }
+  GLIBC_2.20 {
+    # f*
+    fmemopen;
+  }
   GLIBC_PRIVATE {
     # Used by NPTL and librt
     __libc_fatal;
diff --git a/libio/fmemopen.c b/libio/fmemopen.c
index aee2696..53e9d57 100644
--- a/libio/fmemopen.c
+++ b/libio/fmemopen.c
@@ -1,7 +1,6 @@ 
-/* Fmemopen implementation.
-   Copyright (C) 2000-2014 Free Software Foundation, Inc.
+/* fmemopen implementation.
+   Copyright (C) 2014 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
-   Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
 
    The GNU C Library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
@@ -17,54 +16,10 @@ 
    License along with the GNU C Library; if not, see
    <http://www.gnu.org/licenses/>.  */
 
-/*
- * fmemopen() - "my" version of a string stream
- * Hanno Mueller, kontakt@hanno.de
- *
- *
- * I needed fmemopen() for an application that I currently work on,
- * but couldn't find it in libio. The following snippet of code is an
- * attempt to implement what glibc's documentation describes.
- *
- *
- *
- * I already see some potential problems:
- *
- * - I never used the "original" fmemopen(). I am sure that "my"
- *   fmemopen() behaves differently than the original version.
- *
- * - The documentation doesn't say wether a string stream allows
- *   seeks. I checked the old fmemopen implementation in glibc's stdio
- *   directory, wasn't quite able to see what is going on in that
- *   source, but as far as I understand there was no seek there. For
- *   my application, I needed fseek() and ftell(), so it's here.
- *
- * - "append" mode and fseek(p, SEEK_END) have two different ideas
- *   about the "end" of the stream.
- *
- *   As described in the documentation, when opening the file in
- *   "append" mode, the position pointer will be set to the first null
- *   character of the string buffer (yet the buffer may already
- *   contain more data). For fseek(), the last byte of the buffer is
- *   used as the end of the stream.
- *
- * - It is unclear to me what the documentation tries to say when it
- *   explains what happens when you use fmemopen with a NULL
- *   buffer.
- *
- *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
- *   is really only useful if you are going to write things to the
- *   buffer and then read them back in again."
- *
- *   What does that mean if the original fmemopen() did not allow
- *   seeking? How do you read what you just wrote without seeking back
- *   to the beginning of the stream?
- *
- * - I think there should be a second version of fmemopen() that does
- *   not add null characters for each write. (At least in my
- *   application, I am not actually using strings but binary data and
- *   so I don't need the stream to add null characters on its own.)
- */
+/* fmemopen() from 2.20 and forward works as defined by POSIX.  It also
+   provides an older symbol, version 2.2.5, that behaves different regarding
+   SEEK_END (libio/oldfmemopen.c).  */
+
 
 #include <errno.h>
 #include <libio.h>
@@ -79,25 +34,23 @@ 
 typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
 struct fmemopen_cookie_struct
 {
-  char *buffer;
-  int mybuffer;
-  int binmode;
-  size_t size;
-  _IO_off64_t pos;
-  size_t maxpos;
+  char        *buffer;   /* memory buffer.  */
+  int         mybuffer;  /* own allocated buffer?  */
+  int         append;    /* buffer openened for append?  */
+  size_t      size;      /* buffer length in bytes.  */
+  _IO_off64_t pos;       /* current position at the buffer.  */
+  size_t      maxpos;    /* max position in buffer.  */
 };
 
 
 static ssize_t
 fmemopen_read (void *cookie, char *b, size_t s)
 {
-  fmemopen_cookie_t *c;
-
-  c = (fmemopen_cookie_t *) cookie;
+  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
 
-  if (c->pos + s > c->size)
+  if (c->pos + s > c->maxpos)
     {
-      if ((size_t) c->pos == c->size)
+      if ((size_t) c->pos == c->maxpos)
 	return 0;
       s = c->size - c->pos;
     }
@@ -115,29 +68,28 @@  fmemopen_read (void *cookie, char *b, size_t s)
 static ssize_t
 fmemopen_write (void *cookie, const char *b, size_t s)
 {
-  fmemopen_cookie_t *c;
+  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;;
+  _IO_off64_t pos = c->append ? c->maxpos : c->pos;
   int addnullc;
 
-  c = (fmemopen_cookie_t *) cookie;
-
-  addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
+  addnullc = (s == 0 || b[s - 1] != '\0');
 
-  if (c->pos + s + addnullc > c->size)
+  if (pos + s + addnullc > c->size)
     {
-      if ((size_t) (c->pos + addnullc) == c->size)
+      if ((size_t) (pos + addnullc) >= c->size)
 	{
 	  __set_errno (ENOSPC);
 	  return 0;
 	}
-      s = c->size - c->pos - addnullc;
+      s = c->size - pos - addnullc;
     }
 
-  memcpy (&(c->buffer[c->pos]), b, s);
+  memcpy (&(c->buffer[pos]), b, s);
 
-  c->pos += s;
-  if ((size_t) c->pos > c->maxpos)
+  pos += s;
+  if ((size_t) pos > c->maxpos)
     {
-      c->maxpos = c->pos;
+      c->maxpos = pos;
       if (addnullc)
 	c->buffer[c->maxpos] = '\0';
     }
@@ -150,9 +102,7 @@  static int
 fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
 {
   _IO_off64_t np;
-  fmemopen_cookie_t *c;
-
-  c = (fmemopen_cookie_t *) cookie;
+  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
 
   switch (w)
     {
@@ -165,7 +115,7 @@  fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
       break;
 
     case SEEK_END:
-      np = (c->binmode ? c->size : c->maxpos) - *p;
+      np = c->maxpos + *p;
       break;
 
     default:
@@ -184,9 +134,7 @@  fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
 static int
 fmemopen_close (void *cookie)
 {
-  fmemopen_cookie_t *c;
-
-  c = (fmemopen_cookie_t *) cookie;
+  fmemopen_cookie_t *c = (fmemopen_cookie_t *) cookie;
 
   if (c->mybuffer)
     free (c->buffer);
@@ -197,18 +145,11 @@  fmemopen_close (void *cookie)
 
 
 FILE *
-fmemopen (void *buf, size_t len, const char *mode)
+__fmemopen (void *buf, size_t len, const char *mode)
 {
   cookie_io_functions_t iof;
   fmemopen_cookie_t *c;
 
-  if (__glibc_unlikely (len == 0))
-    {
-    einval:
-      __set_errno (EINVAL);
-      return NULL;
-    }
-
   c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
   if (c == NULL)
     return NULL;
@@ -231,26 +172,38 @@  fmemopen (void *buf, size_t len, const char *mode)
       if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
 	{
 	  free (c);
-	  goto einval;
+	  __set_errno (EINVAL);
+	  return NULL;
 	}
 
       c->buffer = buf;
 
-      if (mode[0] == 'w')
+      /* POSIX states that w+ mode should truncate the buffer.  */
+      if (mode[0] == 'w' && mode[1] == '+')
 	c->buffer[0] = '\0';
 
       c->maxpos = strnlen (c->buffer, len);
     }
 
+
+  /* Mode   |  starting position (cookie::pos) |          size (cookie::size)
+   * ------ |----------------------------------|-----------------------------
+   * read   |          beginning of the buffer |                size argument
+   * write  |          beginning of the buffer |                         zero
+   * append |    first null or size buffer + 1 |  first null or size argument
+   */
+
   c->size = len;
 
-  if (mode[0] == 'a')
+  if (mode[0] == 'r')
+    c->maxpos = len;
+
+  c->append = mode[0] == 'a';
+  if (c->append)
     c->pos = c->maxpos;
   else
     c->pos = 0;
 
-  c->binmode = mode[0] != '\0' && mode[1] == 'b';
-
   iof.read = fmemopen_read;
   iof.write = fmemopen_write;
   iof.seek = fmemopen_seek;
@@ -258,4 +211,5 @@  fmemopen (void *buf, size_t len, const char *mode)
 
   return _IO_fopencookie (c, mode, iof);
 }
-libc_hidden_def (fmemopen)
+libc_hidden_def (__fmemopen)
+versioned_symbol (libc, __fmemopen, fmemopen, GLIBC_2_20);
diff --git a/libio/oldfmemopen.c b/libio/oldfmemopen.c
new file mode 100644
index 0000000..4e85ebe
--- /dev/null
+++ b/libio/oldfmemopen.c
@@ -0,0 +1,265 @@ 
+/* Fmemopen implementation.
+   Copyright (C) 2000-2014 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contributed by Hanno Mueller, kontakt@hanno.de, 2000.
+
+   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
+   <http://www.gnu.org/licenses/>.  */
+
+/*
+ * fmemopen() - "my" version of a string stream
+ * Hanno Mueller, kontakt@hanno.de
+ *
+ *
+ * I needed fmemopen() for an application that I currently work on,
+ * but couldn't find it in libio. The following snippet of code is an
+ * attempt to implement what glibc's documentation describes.
+ *
+ *
+ *
+ * I already see some potential problems:
+ *
+ * - I never used the "original" fmemopen(). I am sure that "my"
+ *   fmemopen() behaves differently than the original version.
+ *
+ * - The documentation doesn't say wether a string stream allows
+ *   seeks. I checked the old fmemopen implementation in glibc's stdio
+ *   directory, wasn't quite able to see what is going on in that
+ *   source, but as far as I understand there was no seek there. For
+ *   my application, I needed fseek() and ftell(), so it's here.
+ *
+ * - "append" mode and fseek(p, SEEK_END) have two different ideas
+ *   about the "end" of the stream.
+ *
+ *   As described in the documentation, when opening the file in
+ *   "append" mode, the position pointer will be set to the first null
+ *   character of the string buffer (yet the buffer may already
+ *   contain more data). For fseek(), the last byte of the buffer is
+ *   used as the end of the stream.
+ *
+ * - It is unclear to me what the documentation tries to say when it
+ *   explains what happens when you use fmemopen with a NULL
+ *   buffer.
+ *
+ *   Quote: "fmemopen [then] allocates an array SIZE bytes long. This
+ *   is really only useful if you are going to write things to the
+ *   buffer and then read them back in again."
+ *
+ *   What does that mean if the original fmemopen() did not allow
+ *   seeking? How do you read what you just wrote without seeking back
+ *   to the beginning of the stream?
+ *
+ * - I think there should be a second version of fmemopen() that does
+ *   not add null characters for each write. (At least in my
+ *   application, I am not actually using strings but binary data and
+ *   so I don't need the stream to add null characters on its own.)
+ */
+
+#include "libioP.h"
+
+#if SHLIB_COMPAT (libc, GLIBC_2_2, GLIBC_2_20)
+
+#include <errno.h>
+#include <libio.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/types.h>
+
+
+typedef struct fmemopen_cookie_struct fmemopen_cookie_t;
+struct fmemopen_cookie_struct
+{
+  char *buffer;
+  int mybuffer;
+  int binmode;
+  size_t size;
+  _IO_off64_t pos;
+  size_t maxpos;
+};
+
+
+static ssize_t
+fmemopen_read (void *cookie, char *b, size_t s)
+{
+  fmemopen_cookie_t *c;
+
+  c = (fmemopen_cookie_t *) cookie;
+
+  if (c->pos + s > c->size)
+    {
+      if ((size_t) c->pos == c->size)
+	return 0;
+      s = c->size - c->pos;
+    }
+
+  memcpy (b, &(c->buffer[c->pos]), s);
+
+  c->pos += s;
+  if ((size_t) c->pos > c->maxpos)
+    c->maxpos = c->pos;
+
+  return s;
+}
+
+
+static ssize_t
+fmemopen_write (void *cookie, const char *b, size_t s)
+{
+  fmemopen_cookie_t *c;
+  int addnullc;
+
+  c = (fmemopen_cookie_t *) cookie;
+
+  addnullc = c->binmode == 0 && (s == 0 || b[s - 1] != '\0');
+
+  if (c->pos + s + addnullc > c->size)
+    {
+      if ((size_t) (c->pos + addnullc) == c->size)
+	{
+	  __set_errno (ENOSPC);
+	  return 0;
+	}
+      s = c->size - c->pos - addnullc;
+    }
+
+  memcpy (&(c->buffer[c->pos]), b, s);
+
+  c->pos += s;
+  if ((size_t) c->pos > c->maxpos)
+    {
+      c->maxpos = c->pos;
+      if (addnullc)
+	c->buffer[c->maxpos] = '\0';
+    }
+
+  return s;
+}
+
+
+static int
+fmemopen_seek (void *cookie, _IO_off64_t *p, int w)
+{
+  _IO_off64_t np;
+  fmemopen_cookie_t *c;
+
+  c = (fmemopen_cookie_t *) cookie;
+
+  switch (w)
+    {
+    case SEEK_SET:
+      np = *p;
+      break;
+
+    case SEEK_CUR:
+      np = c->pos + *p;
+      break;
+
+    case SEEK_END:
+      np = (c->binmode ? c->size : c->maxpos) - *p;
+      break;
+
+    default:
+      return -1;
+    }
+
+  if (np < 0 || (size_t) np > c->size)
+    return -1;
+
+  *p = c->pos = np;
+
+  return 0;
+}
+
+
+static int
+fmemopen_close (void *cookie)
+{
+  fmemopen_cookie_t *c;
+
+  c = (fmemopen_cookie_t *) cookie;
+
+  if (c->mybuffer)
+    free (c->buffer);
+  free (c);
+
+  return 0;
+}
+
+
+FILE *
+__old_fmemopen (void *buf, size_t len, const char *mode)
+{
+  cookie_io_functions_t iof;
+  fmemopen_cookie_t *c;
+
+  if (__glibc_unlikely (len == 0))
+    {
+    einval:
+      __set_errno (EINVAL);
+      return NULL;
+    }
+
+  c = (fmemopen_cookie_t *) malloc (sizeof (fmemopen_cookie_t));
+  if (c == NULL)
+    return NULL;
+
+  c->mybuffer = (buf == NULL);
+
+  if (c->mybuffer)
+    {
+      c->buffer = (char *) malloc (len);
+      if (c->buffer == NULL)
+	{
+	  free (c);
+	  return NULL;
+	}
+      c->buffer[0] = '\0';
+      c->maxpos = 0;
+    }
+  else
+    {
+      if (__glibc_unlikely ((uintptr_t) len > -(uintptr_t) buf))
+	{
+	  free (c);
+	  goto einval;
+	}
+
+      c->buffer = buf;
+
+      if (mode[0] == 'w')
+	c->buffer[0] = '\0';
+
+      c->maxpos = strnlen (c->buffer, len);
+    }
+
+  c->size = len;
+
+  if (mode[0] == 'a')
+    c->pos = c->maxpos;
+  else
+    c->pos = 0;
+
+  c->binmode = mode[0] != '\0' && mode[1] == 'b';
+
+  iof.read = fmemopen_read;
+  iof.write = fmemopen_write;
+  iof.seek = fmemopen_seek;
+  iof.close = fmemopen_close;
+
+  return _IO_fopencookie (c, mode, iof);
+}
+compat_symbol (libc, __old_fmemopen, fmemopen, GLIBC_2_2);
+#endif
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 5f8e534..5971e65 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -57,7 +57,7 @@  tests := tstscanf test_rdwr test-popen tstgetln test-fseek \
 	 bug19 bug19a tst-popen2 scanf13 scanf14 scanf15 bug20 bug21 bug22 \
 	 scanf16 scanf17 tst-setvbuf1 tst-grouping bug23 bug24 \
 	 bug-vfprintf-nargs tst-long-dbl-fphex tst-fphex-wide tst-sprintf3 \
-	 bug25 tst-printf-round bug26
+	 bug25 tst-printf-round bug26 tst-fmemopen3
 
 test-srcs = tst-unbputc tst-printf
 
diff --git a/stdio-common/psiginfo.c b/stdio-common/psiginfo.c
index 564d237..62a9671 100644
--- a/stdio-common/psiginfo.c
+++ b/stdio-common/psiginfo.c
@@ -60,7 +60,7 @@  void
 psiginfo (const siginfo_t *pinfo, const char *s)
 {
   char buf[512];
-  FILE *fp = fmemopen (buf, sizeof (buf), "w");
+  FILE *fp = __fmemopen (buf, sizeof (buf), "w");
   if (fp == NULL)
     {
       const char *colon;
diff --git a/stdio-common/tst-fmemopen3.c b/stdio-common/tst-fmemopen3.c
new file mode 100644
index 0000000..1ff0920
--- /dev/null
+++ b/stdio-common/tst-fmemopen3.c
@@ -0,0 +1,206 @@ 
+/* fmemopen tests for append and read mode.
+   Copyright (C) 2014 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
+   <http://www.gnu.org/licenses/>.  */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+
+static void
+print_buffer (const char *s, size_t n)
+{
+  size_t i;
+  for (i=0; i<n; ++i)
+    printf ("0x%02X (%c), ", s[i], s[i]);
+}
+
+/* This test check append mode initial position (a/a+) based on POSIX defition
+   (BZ#6544 and BZ#13151).  */
+static int
+do_test_write_append (const char *mode)
+{
+  char buf[32] = "testing buffer";
+  char exp[32] = "testing bufferXX";
+
+  FILE *fp = fmemopen (buf, sizeof (buf), mode);
+
+  fflush (fp);
+  fprintf (fp, "X");
+  fseek (fp, 0, SEEK_SET);
+  fprintf (fp, "X");
+  fclose (fp);
+
+  if (strcmp (buf, exp) != 0)
+    {
+      printf ("%s: check failed: %s != %s\n", __FUNCTION__, buf, exp);
+      return 1;
+    }
+
+  return 0;
+}
+
+/* This test check append mode initial position (a/a+) based on POSIX defition
+   (BZ#6544 and BZ#13151) for buffer without null byte end.  */
+static int
+do_test_write_append_without_null (const char *mode)
+{
+  char buf[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
+  char exp[] = { 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55 };
+
+  /* If '\0' is not found in buffer, POSIX states that SEEK_SET should be
+     the size argument.  */
+  FILE *fp = fmemopen (buf, sizeof (buf) - 2, "a");
+
+  fflush (fp);
+  fputc (0x70, fp);
+  fseek (fp, 0, SEEK_SET);
+  fputc (0x70, fp);
+  fputc (0x70, fp);
+  fclose (fp);
+
+  /* POSIX also states that a write operation on the stream shall not advance
+     the current buffer size beyond the size given in fmemopen, so the string
+     should be same.  */
+  if (memcmp (buf, exp, sizeof (buf)) != 0)
+    {
+      printf ("%s: check failed: ", __FUNCTION__);
+      print_buffer (buf, sizeof (buf));
+      printf ("!= ");
+      print_buffer (exp, sizeof (exp));
+      printf ("\n");
+      return 1;
+    }
+
+  return 0;
+}
+
+/* This test check for initial position and feek value for fmemopen objects
+   opened with append mode.  */
+static int
+do_test_read_append (void)
+{
+  char buf[32] = "testing buffer";
+  size_t buflen = strlen (buf);
+  long fpos;
+
+  /* POSIX defines for 'a+' the initial position is the first null byte.  */
+  FILE *fp = fmemopen (buf, sizeof (buf), "a+");
+
+  fpos = ftell (fp);
+  if (fpos != buflen)
+    {
+      printf ("%s: ftell|SEEK_SET (fp) %li != strlen (%s) %li\n",
+	      __FUNCTION__, fpos, buf, buflen);
+      fclose (fp);
+      return 1;
+    }
+
+  fseek (fp, 0, SEEK_END);
+
+  if (fpos != buflen)
+    {
+      printf ("%s: ftell|SEEK_END (fp) %li != strlen (%s) %li\n",
+	      __FUNCTION__, fpos, buf, buflen);
+      fclose (fp);
+      return 1;
+    }
+  fclose (fp);
+
+  /* Check if attempting to read past the current size, defined as strlen (buf)
+     yield an EOF.  */
+  fp = fmemopen (buf, sizeof (buf), "a+");
+  if (getc(fp) != EOF)
+    {
+      printf ("%s: getc(fp) != EOF\n", __FUNCTION__);
+      fclose (fp);
+      return -1;
+    }
+
+  fclose (fp);
+
+  return 0;
+}
+
+/* This test check for fseek (SEEK_END) using negative offsets (BZ#14292).  The
+   starting position of descriptor is different base on the opening mode.  */
+static int
+do_test_read_seek_neg (const char *mode, const char *expected)
+{
+  char buf[] = "abcdefghijklmnopqrstuvxz0123456789";
+  char tmp[10];
+  size_t tmps = sizeof (tmps);
+  long offset = -11;
+
+  FILE *fp = fmemopen (buf, sizeof (buf), mode);
+  fseek (fp, offset, SEEK_END);
+  fread (tmp, tmps, 1, fp);
+  
+  if (memcmp (tmp, expected, tmps) != 0)
+    {
+      printf ("%s: fmemopen(%s) - fseek (fp, %li, SEEK_END):\n",
+	      __FUNCTION__, mode, offset);
+      printf ("  returned: ");
+      print_buffer (tmp, tmps);
+      printf ("\n");
+      printf ("  expected: ");
+      print_buffer (expected, tmps);
+      printf ("\n");
+      return 1;
+    }
+  
+  fclose (fp);
+
+  return 0;
+}
+
+static int
+do_test_read_seek_negative (void)
+{
+  int ret = 0;
+
+  /* 'r' and 'w' modes defines the initial position at the buffer start and
+     seek with SEEK_END shall seek relative to its size give in fmemopen
+     call.  The expected tmp result is 0 to 9 *without* the ending null  */
+  ret += do_test_read_seek_neg ("r", "0123456789");
+  /* 'a+' mode sets the initial position at the first null byte in buffer and
+    SEEK_END shall seek relative to its size as well.  The expected result is
+    z012345678, since SEEK_END plus a+ start at '\0', not size.  */
+  ret += do_test_read_seek_neg ("a+", "z012345678");
+
+  return ret;
+}
+
+static int
+do_test (void)
+{
+  int ret = 0;
+
+  ret += do_test_write_append ("a");
+  ret += do_test_write_append_without_null ("a");
+  ret += do_test_write_append ("a+");
+  ret += do_test_write_append_without_null ("a+");
+
+  ret += do_test_read_append ();
+
+  ret += do_test_read_seek_negative ();
+
+  return ret;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/sysdeps/unix/sysv/linux/aarch64/libc.abilist b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
index aeee312..6cf2ddd 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/aarch64/libc.abilist
@@ -2081,3 +2081,6 @@  GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
  _mcount F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
diff --git a/sysdeps/unix/sysv/linux/alpha/libc.abilist b/sysdeps/unix/sysv/linux/alpha/libc.abilist
index 980e088..11f03a5 100644
--- a/sysdeps/unix/sysv/linux/alpha/libc.abilist
+++ b/sysdeps/unix/sysv/linux/alpha/libc.abilist
@@ -1822,6 +1822,9 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.2
  GLIBC_2.2 A
  _IO_adjust_wcolumn F
diff --git a/sysdeps/unix/sysv/linux/arm/libc.abilist b/sysdeps/unix/sysv/linux/arm/libc.abilist
index ce45208..0a6ef89 100644
--- a/sysdeps/unix/sysv/linux/arm/libc.abilist
+++ b/sysdeps/unix/sysv/linux/arm/libc.abilist
@@ -89,6 +89,9 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.4
  GLIBC_2.4 A
  _Exit F
diff --git a/sysdeps/unix/sysv/linux/i386/libc.abilist b/sysdeps/unix/sysv/linux/i386/libc.abilist
index 3cb314d..af60934 100644
--- a/sysdeps/unix/sysv/linux/i386/libc.abilist
+++ b/sysdeps/unix/sysv/linux/i386/libc.abilist
@@ -2023,6 +2023,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist b/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
index 067552d..afe6f4b 100644
--- a/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
+++ b/sysdeps/unix/sysv/linux/ia64/nptl/libc.abilist
@@ -1881,6 +1881,9 @@  GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
  getunwind F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
index f06cc8e..2bc1ce4 100644
--- a/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/coldfire/libc.abilist
@@ -90,6 +90,9 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.4
  GLIBC_2.4 A
  _Exit F
diff --git a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
index 9010ea7..48c25b4 100644
--- a/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
+++ b/sysdeps/unix/sysv/linux/m68k/m680x0/libc.abilist
@@ -1979,6 +1979,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/microblaze/libc.abilist b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
index 6e8d993..8b506ed 100644
--- a/sysdeps/unix/sysv/linux/microblaze/libc.abilist
+++ b/sysdeps/unix/sysv/linux/microblaze/libc.abilist
@@ -2080,3 +2080,6 @@  GLIBC_2.18
  xencrypt F
  xprt_register F
  xprt_unregister F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
index 1c3490c..eb7860e 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/fpu/libc.abilist
@@ -1951,6 +1951,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
index d8fd823..e4385d2 100644
--- a/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips32/nofpu/libc.abilist
@@ -1949,6 +1949,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
index 3e6ed35..44b4f20 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n32/libc.abilist
@@ -1947,6 +1947,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
index c7e46aa..2c00ad7 100644
--- a/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/libc.abilist
@@ -1941,6 +1941,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
index f27b48b..29b9695 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/fpu/libc.abilist
@@ -1983,6 +1983,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
index a54382e..b57f378 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc32/nofpu/libc.abilist
@@ -1989,6 +1989,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
index 195b587..7a1deff 100644
--- a/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/powerpc/powerpc64/libc.abilist
@@ -90,6 +90,9 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  _Exit F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
index 03f2e83..d23377d 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-32/libc.abilist
@@ -1985,6 +1985,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
index 4576fc8..7a4081e 100644
--- a/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/s390/s390-64/libc.abilist
@@ -1881,6 +1881,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/sh/libc.abilist b/sysdeps/unix/sysv/linux/sh/libc.abilist
index a653292..51f8707 100644
--- a/sysdeps/unix/sysv/linux/sh/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sh/libc.abilist
@@ -1864,6 +1864,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
index 9defbdf..46490a7 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc32/libc.abilist
@@ -1975,6 +1975,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
index 35987fa..540bc1c 100644
--- a/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/sparc/sparc64/libc.abilist
@@ -1908,6 +1908,9 @@  GLIBC_2.2.4
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
index caf74b8..a473437 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx32/libc.abilist
@@ -2091,3 +2091,6 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
diff --git a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
index 68d975b..16bd028 100644
--- a/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilegx/tilegx64/libc.abilist
@@ -2091,3 +2091,6 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
diff --git a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
index caf74b8..a473437 100644
--- a/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
+++ b/sysdeps/unix/sysv/linux/tile/tilepro/libc.abilist
@@ -2091,3 +2091,6 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
diff --git a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
index 914b590..b77feba 100644
--- a/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/64/libc.abilist
@@ -1854,6 +1854,9 @@  GLIBC_2.2.5
 GLIBC_2.2.6
  GLIBC_2.2.6 A
  __nanosleep F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F
 GLIBC_2.3
  GLIBC_2.3 A
  __ctype_b_loc F
diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
index 0f64c8d..f638884 100644
--- a/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
+++ b/sysdeps/unix/sysv/linux/x86_64/x32/libc.abilist
@@ -2089,3 +2089,6 @@  GLIBC_2.17
 GLIBC_2.18
  GLIBC_2.18 A
  __cxa_thread_atexit_impl F
+GLIBC_2.20
+ GLIBC_2.20 A
+ fmemopen F