@@ -26,12 +26,15 @@ struct _IO_FILE_memstream
_IO_strfile _sf;
char **bufloc;
_IO_size_t *sizeloc;
+ char *prevwriteptr;
+ char *seekwriteptr;
};
static int _IO_mem_sync (_IO_FILE* fp) __THROW;
static void _IO_mem_finish (_IO_FILE* fp, int) __THROW;
-
+static _IO_off64_t _IO_mem_seekoff (_IO_FILE *fp, _IO_off64_t offset,
+ int dir, int mode) __THROW;
static const struct _IO_jump_t _IO_mem_jumps libio_vtable =
{
@@ -43,7 +46,7 @@ static const struct _IO_jump_t _IO_mem_jumps libio_vtable =
JUMP_INIT (pbackfail, _IO_str_pbackfail),
JUMP_INIT (xsputn, _IO_default_xsputn),
JUMP_INIT (xsgetn, _IO_default_xsgetn),
- JUMP_INIT (seekoff, _IO_str_seekoff),
+ JUMP_INIT (seekoff, _IO_mem_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_default_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
@@ -95,6 +98,24 @@ __open_memstream (char **bufloc, _IO_size_t *sizeloc)
new_f->fp.bufloc = bufloc;
new_f->fp.sizeloc = sizeloc;
+ /* To correctly report the buffer size the implementation must track both
+ the buffer size and currently bytes written. However _IO_write_ptr is
+ updated on both write and seek operations. So to track current written
+ bytes two fields are used:
+
+ - prevwriteptr: track previous _IO_write_ptr before a seek operation on
+ the stream.
+ - seekwriteptr: track resulted _IO_write_ptr after a seek operation on
+ the stream.
+
+ Also, prevwriteptr is only updated iff _IO_write_ptr changed over calls
+ (meaning that a write operation occured)
+
+ So final buffer size is based on current _IO_write_ptr only if
+ its value is different than seekwriteptr, otherwise it uses the old
+ _IO_write_ptr value before seek operation (prevwriteptr). */
+ new_f->fp.prevwriteptr = new_f->fp.seekwriteptr =
+ new_f->fp._sf._sbf._f._IO_write_ptr;
return (_IO_FILE *) &new_f->fp._sf._sbf;
}
@@ -114,7 +135,9 @@ _IO_mem_sync (_IO_FILE *fp)
}
*mp->bufloc = fp->_IO_write_base;
- *mp->sizeloc = fp->_IO_write_ptr - fp->_IO_write_base;
+ char *ptr = (fp->_IO_write_ptr == mp->seekwriteptr)
+ ? mp->prevwriteptr : fp->_IO_write_ptr;
+ *mp->sizeloc = ptr - fp->_IO_write_base;
return 0;
}
@@ -129,11 +152,30 @@ _IO_mem_finish (_IO_FILE *fp, int dummy)
fp->_IO_write_ptr - fp->_IO_write_base + 1);
if (*mp->bufloc != NULL)
{
- (*mp->bufloc)[fp->_IO_write_ptr - fp->_IO_write_base] = '\0';
- *mp->sizeloc = fp->_IO_write_ptr - fp->_IO_write_base;
+ size_t len;
+ if (fp->_IO_write_ptr == mp->seekwriteptr)
+ len = mp->prevwriteptr - fp->_IO_write_base;
+ else
+ {
+ /* An '\0' should be appended iff a write operation ocurred. */
+ len = fp->_IO_write_ptr - fp->_IO_write_base;
+ (*mp->bufloc)[len] = '\0';
+ }
+ *mp->sizeloc = len;
fp->_IO_buf_base = NULL;
}
_IO_str_finish (fp, 0);
}
+
+static _IO_off64_t
+_IO_mem_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
+{
+ struct _IO_FILE_memstream *mp = (struct _IO_FILE_memstream *) fp;
+ if (fp->_IO_write_ptr != mp->seekwriteptr)
+ mp->prevwriteptr = fp->_IO_write_ptr;
+ _IO_off64_t ret = _IO_str_seekoff (fp, offset, dir, mode);
+ mp->seekwriteptr = fp->_IO_write_ptr;
+ return ret;
+}
@@ -47,6 +47,69 @@ mcheck_abort (enum mcheck_status ev)
{ printf(LOC1(__LINE__) ": " __VA_ARGS__); return 1; }
static int
+do_test_bz15298 (void)
+{
+ CHAR_T *buf;
+ size_t size;
+ size_t ret;
+
+ FILE *fp = OPEN_MEMSTREAM (&buf, &size);
+ if (fp == NULL)
+ ERROR_RET1 ("%s failed\n", S(OPEN_MEMSTREAM));
+
+ /* Move internal position but do not write any bytes. Final size should
+ be 0. */
+ if (fseek (fp, 10, SEEK_SET) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fseek (fp, 20, SEEK_CUR) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fseek (fp, 30, SEEK_CUR) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fflush (fp) != 0)
+ ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
+ if (size != 0)
+ ERROR_RET1 ("size != 0 (got %zu)\n", size);
+
+ /* Now write some bytes and change internal position. Final size should
+ be based on written bytes. */
+ if (fseek (fp, 0, SEEK_SET) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if ((ret = FWRITE (W("abc"), 1, 3, fp)) != 3)
+ ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);
+ if (fseek (fp, 20, SEEK_CUR) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fseek (fp, 30, SEEK_CUR) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fflush (fp) != 0)
+ ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
+ if (size != 3)
+ ERROR_RET1 ("size != 3 (got %zu)\n", size);
+
+ /* Finally set position, write some bytes and change position again. Final
+ size should be based again on write position. */
+ size_t offset = 2048;
+ if (fseek (fp, offset, SEEK_SET) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if ((ret = FWRITE (W("abc"), 1, 3, fp)) != 3)
+ ERROR_RET1 ("%s failed (errno = %d)\n", S(FWRITE), errno);
+ if (fseek (fp, 20, SEEK_CUR) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fseek (fp, 30, SEEK_CUR) == -1)
+ ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
+ if (fflush (fp) != 0)
+ ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
+ if (size != offset + 3)
+ ERROR_RET1 ("size != %zu (got %zu)\n", offset + 3, size);
+
+ if (fclose (fp) != 0)
+ ERROR_RET1 ("fclose failed (errno = %d\n", errno);
+
+ free (buf);
+
+ return 0;
+}
+
+static int
do_test_bz18241 (void)
{
CHAR_T *buf;
@@ -114,15 +177,17 @@ do_test_bz20181 (void)
if (fflush (fp) != 0)
ERROR_RET1 ("fflush failed (errno = %d)\n", errno);
- /* Avoid truncating the buffer on close. */
+ /* fseek updates the internal buffer, but open_memstream should set the
+ size to smaller of the buffer size and number of bytes written. Since
+ it was written just character ('z') final size should be 1. */
if (fseek (fp, 3, SEEK_SET) != 0)
ERROR_RET1 ("fseek failed (errno = %d)\n", errno);
if (fclose (fp) != 0)
ERROR_RET1 ("fclose failed (errno = %d\n", errno);
- if (size != 3)
- ERROR_RET1 ("size != 3\n");
+ if (size != 1)
+ ERROR_RET1 ("size != 1 (got %zu)\n", size);
if (buf[0] != W('z')
|| buf[1] != W('b')
@@ -145,6 +210,7 @@ do_test (void)
mcheck_pedantic (mcheck_abort);
+ ret += do_test_bz15298 ();
ret += do_test_bz18241 ();
ret += do_test_bz20181 ();
@@ -27,12 +27,15 @@ struct _IO_FILE_wmemstream
_IO_strfile _sf;
wchar_t **bufloc;
_IO_size_t *sizeloc;
+ wchar_t *prevwriteptr;
+ wchar_t *seekwriteptr;
};
static int _IO_wmem_sync (_IO_FILE* fp) __THROW;
static void _IO_wmem_finish (_IO_FILE* fp, int) __THROW;
-
+static _IO_off64_t _IO_wmem_seekoff (_IO_FILE *fp, _IO_off64_t offset,
+ int dir, int mode) __THROW;
static const struct _IO_jump_t _IO_wmem_jumps libio_vtable =
{
@@ -44,7 +47,7 @@ static const struct _IO_jump_t _IO_wmem_jumps libio_vtable =
JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
JUMP_INIT (xsputn, _IO_wdefault_xsputn),
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
- JUMP_INIT (seekoff, _IO_wstr_seekoff),
+ JUMP_INIT (seekoff, _IO_wmem_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_default_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
@@ -97,6 +100,24 @@ open_wmemstream (wchar_t **bufloc, _IO_size_t *sizeloc)
new_f->fp.bufloc = bufloc;
new_f->fp.sizeloc = sizeloc;
+ /* To correctly report the buffer size the implementation must track both
+ the buffer size and currently bytes written. However _IO_write_ptr is
+ updated on both write and seek operations. So to track current written
+ bytes two fields are used:
+
+ - prevwriteptr: track previous _IO_write_ptr before a seek operation on
+ the stream.
+ - seekwriteptr: track resulted _IO_write_ptr after a seek operation on
+ the stream.
+
+ Also, prevwriteptr is only updated iff _IO_write_ptr changed over calls
+ (meaning that a write operation occured)
+
+ So final buffer size is based on current _IO_write_ptr only if
+ its value is different than seekwriteptr, otherwise it uses the old
+ _IO_write_ptr value before seek operation (prevwriteptr). */
+ new_f->fp.prevwriteptr = new_f->fp.seekwriteptr =
+ new_f->fp._sf._sbf._f._wide_data->_IO_write_ptr;
return (_IO_FILE *) &new_f->fp._sf._sbf;
}
@@ -114,8 +135,9 @@ _IO_wmem_sync (_IO_FILE *fp)
}
*mp->bufloc = fp->_wide_data->_IO_write_base;
- *mp->sizeloc = (fp->_wide_data->_IO_write_ptr
- - fp->_wide_data->_IO_write_base);
+ wchar_t *ptr = (fp->_wide_data->_IO_write_ptr == mp->seekwriteptr)
+ ? mp->prevwriteptr : fp->_wide_data->_IO_write_ptr;
+ *mp->sizeloc = ptr - fp->_wide_data->_IO_write_base;
return 0;
}
@@ -132,9 +154,16 @@ _IO_wmem_finish (_IO_FILE *fp, int dummy)
* sizeof (wchar_t));
if (*mp->bufloc != NULL)
{
- size_t len = (fp->_wide_data->_IO_write_ptr
- - fp->_wide_data->_IO_write_base);
- (*mp->bufloc)[len] = '\0';
+ size_t len;
+ if (fp->_wide_data->_IO_write_ptr == mp->seekwriteptr)
+ len = mp->prevwriteptr - fp->_wide_data->_IO_write_base;
+ else
+ {
+ /* An '\0' should be appended iff a write operation ocurred. */
+ len = fp->_wide_data->_IO_write_ptr
+ - fp->_wide_data->_IO_write_base;
+ (*mp->bufloc)[len] = L'\0';
+ }
*mp->sizeloc = len;
fp->_wide_data->_IO_buf_base = NULL;
@@ -142,3 +171,14 @@ _IO_wmem_finish (_IO_FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+static _IO_off64_t
+_IO_wmem_seekoff (_IO_FILE *fp, _IO_off64_t offset, int dir, int mode)
+{
+ struct _IO_FILE_wmemstream *mp = (struct _IO_FILE_wmemstream *) fp;
+ if (fp->_wide_data->_IO_write_ptr != mp->seekwriteptr)
+ mp->prevwriteptr = fp->_wide_data->_IO_write_ptr;
+ _IO_off64_t ret = _IO_wstr_seekoff (fp, offset, dir, mode);
+ mp->seekwriteptr = fp->_wide_data->_IO_write_ptr;
+ return ret;
+}