mirror of
git://sourceware.org/git/newlib-cygwin.git
synced 2025-01-29 02:20:21 +08:00
3d94e07c49
Previously, fprintf() on a wide-oriented stream crashes or outputs garbage. This is because a narrow char string which can be odd bytes in length is cast into a wide char string which should be even bytes in length in __sprint_r/__sfputs_r based on the __SWID flag. As a result, if the length is odd bytes, the reading buffer runs over the buffer length, which causes a crash. If the length is even bytes, garbage is printed. With this patch, any output to the stream which is set to different orientation fails with error just like glibc. Note that it behaves differently from other libc implementations such as BSD, musl and Solaris. Reviewed-by: Corinna Vinschen <corinna@vinschen.de> Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
219 lines
5.6 KiB
C
219 lines
5.6 KiB
C
/*
|
|
* Copyright (c) 1990 The Regents of the University of California.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms are permitted
|
|
* provided that the above copyright notice and this paragraph are
|
|
* duplicated in all such forms and that any documentation,
|
|
* and/or other materials related to such
|
|
* distribution and use acknowledge that the software was developed
|
|
* by the University of California, Berkeley. The name of the
|
|
* University may not be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
*/
|
|
/*
|
|
FUNCTION
|
|
<<ungetc>>---push data back into a stream
|
|
|
|
INDEX
|
|
ungetc
|
|
INDEX
|
|
_ungetc_r
|
|
|
|
SYNOPSIS
|
|
#include <stdio.h>
|
|
int ungetc(int <[c]>, FILE *<[stream]>);
|
|
|
|
int _ungetc_r(struct _reent *<[reent]>, int <[c]>, FILE *<[stream]>);
|
|
|
|
DESCRIPTION
|
|
<<ungetc>> is used to return bytes back to <[stream]> to be read again.
|
|
If <[c]> is EOF, the stream is unchanged. Otherwise, the unsigned
|
|
char <[c]> is put back on the stream, and subsequent reads will see
|
|
the bytes pushed back in reverse order. Pushed byes are lost if the
|
|
stream is repositioned, such as by <<fseek>>, <<fsetpos>>, or
|
|
<<rewind>>.
|
|
|
|
The underlying file is not changed, but it is possible to push back
|
|
something different than what was originally read. Ungetting a
|
|
character will clear the end-of-stream marker, and decrement the file
|
|
position indicator. Pushing back beyond the beginning of a file gives
|
|
unspecified behavior.
|
|
|
|
The alternate function <<_ungetc_r>> is a reentrant version. The
|
|
extra argument <[reent]> is a pointer to a reentrancy structure.
|
|
|
|
RETURNS
|
|
The character pushed back, or <<EOF>> on error.
|
|
|
|
PORTABILITY
|
|
ANSI C requires <<ungetc>>, but only requires a pushback buffer of one
|
|
byte; although this implementation can handle multiple bytes, not all
|
|
can. Pushing back a signed char is a common application bug.
|
|
|
|
Supporting OS subroutines required: <<sbrk>>.
|
|
*/
|
|
|
|
#if defined(LIBC_SCCS) && !defined(lint)
|
|
static char sccsid[] = "%W% (Berkeley) %G%";
|
|
#endif /* LIBC_SCCS and not lint */
|
|
|
|
#include <_ansi.h>
|
|
#include <reent.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "local.h"
|
|
|
|
/*
|
|
* Expand the ungetc buffer `in place'. That is, adjust fp->_p when
|
|
* the buffer moves, so that it points the same distance from the end,
|
|
* and move the bytes in the buffer around as necessary so that they
|
|
* are all at the end (stack-style).
|
|
*/
|
|
|
|
/*static*/
|
|
int
|
|
__submore (struct _reent *rptr,
|
|
register FILE *fp)
|
|
{
|
|
register int i;
|
|
register unsigned char *p;
|
|
|
|
if (fp->_ub._base == fp->_ubuf)
|
|
{
|
|
/*
|
|
* Get a new buffer (rather than expanding the old one).
|
|
*/
|
|
if ((p = (unsigned char *) _malloc_r (rptr, (size_t) BUFSIZ)) == NULL)
|
|
return EOF;
|
|
fp->_ub._base = p;
|
|
fp->_ub._size = BUFSIZ;
|
|
p += BUFSIZ - sizeof (fp->_ubuf);
|
|
for (i = sizeof (fp->_ubuf); --i >= 0;)
|
|
p[i] = fp->_ubuf[i];
|
|
fp->_p = p;
|
|
return 0;
|
|
}
|
|
i = fp->_ub._size;
|
|
p = (unsigned char *) _realloc_r (rptr, (void *) (fp->_ub._base), i << 1);
|
|
if (p == NULL)
|
|
return EOF;
|
|
(void) memcpy ((void *) (p + i), (void *) p, (size_t) i);
|
|
fp->_p = p + i;
|
|
fp->_ub._base = p;
|
|
fp->_ub._size = i << 1;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
_ungetc_r (struct _reent *rptr,
|
|
int c,
|
|
register FILE *fp)
|
|
{
|
|
if (c == EOF)
|
|
return (EOF);
|
|
|
|
/* Ensure stdio has been initialized.
|
|
??? Might be able to remove this as some other stdio routine should
|
|
have already been called to get the char we are un-getting. */
|
|
|
|
CHECK_INIT (rptr, fp);
|
|
|
|
_newlib_flockfile_start (fp);
|
|
|
|
if (ORIENT (fp, -1) != -1)
|
|
{
|
|
_newlib_flockfile_exit (fp);
|
|
return EOF;
|
|
}
|
|
|
|
/* After ungetc, we won't be at eof anymore */
|
|
fp->_flags &= ~__SEOF;
|
|
|
|
if ((fp->_flags & __SRD) == 0)
|
|
{
|
|
/*
|
|
* Not already reading: no good unless reading-and-writing.
|
|
* Otherwise, flush any current write stuff.
|
|
*/
|
|
if ((fp->_flags & __SRW) == 0)
|
|
{
|
|
_newlib_flockfile_exit (fp);
|
|
return EOF;
|
|
}
|
|
if (fp->_flags & __SWR)
|
|
{
|
|
if (_fflush_r (rptr, fp))
|
|
{
|
|
_newlib_flockfile_exit (fp);
|
|
return EOF;
|
|
}
|
|
fp->_flags &= ~__SWR;
|
|
fp->_w = 0;
|
|
fp->_lbfsize = 0;
|
|
}
|
|
fp->_flags |= __SRD;
|
|
}
|
|
c = (unsigned char) c;
|
|
|
|
/*
|
|
* If we are in the middle of ungetc'ing, just continue.
|
|
* This may require expanding the current ungetc buffer.
|
|
*/
|
|
|
|
if (HASUB (fp))
|
|
{
|
|
if (fp->_r >= fp->_ub._size && __submore (rptr, fp))
|
|
{
|
|
_newlib_flockfile_exit (fp);
|
|
return EOF;
|
|
}
|
|
*--fp->_p = c;
|
|
fp->_r++;
|
|
_newlib_flockfile_exit (fp);
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* If we can handle this by simply backing up, do so,
|
|
* but never replace the original character.
|
|
* (This makes sscanf() work when scanning `const' data.)
|
|
*/
|
|
|
|
if (fp->_bf._base != NULL && fp->_p > fp->_bf._base && fp->_p[-1] == c)
|
|
{
|
|
fp->_p--;
|
|
fp->_r++;
|
|
_newlib_flockfile_exit (fp);
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* Create an ungetc buffer.
|
|
* Initially, we will use the `reserve' buffer.
|
|
*/
|
|
|
|
fp->_ur = fp->_r;
|
|
fp->_up = fp->_p;
|
|
fp->_ub._base = fp->_ubuf;
|
|
fp->_ub._size = sizeof (fp->_ubuf);
|
|
fp->_ubuf[sizeof (fp->_ubuf) - 1] = c;
|
|
fp->_p = &fp->_ubuf[sizeof (fp->_ubuf) - 1];
|
|
fp->_r = 1;
|
|
_newlib_flockfile_end (fp);
|
|
return c;
|
|
}
|
|
|
|
#ifndef _REENT_ONLY
|
|
int
|
|
ungetc (int c,
|
|
register FILE *fp)
|
|
{
|
|
return _ungetc_r (_REENT, c, fp);
|
|
}
|
|
#endif /* !_REENT_ONLY */
|