I'm trying to read a wide char from a stream that was created using fmemopen
with a char *
.
char *s = "foo bar foo";
FILE *f = fmemopen(s,strlen(s),"r");
wchar_t c = getwc(f);
getwc
throws a segmentation fault, I checked using GDB.
I know this is due to opening the stream with fmemopen
, because calling getwc
on a stream opened normally works fine.
Is there a wide char version of fmemopen
, or is there some other way to fix this problem?
The second line should read FILE *f = fmemopen(s, strlen(s), "r");
. As posted, fmemopen
has undefined behavior and might return NULL
, which causes getwc()
to crash.
Changing the fmemopen()
line and adding a check for NULL
fixes the crash but does not meet the OPs goal.
It seems wide orientation is not supported on streams open with fmemopen()
, At least for the GNU C library. Note that fmemopen
is not defined in the C Standard but in POSIX.1-2008 and is not available on many systems (like OS/X).
Here is a corrected and extended version of your program:
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
int main(void) {
const char *s = "foo bar foo";
FILE *f = fmemopen((void *)s, strlen(s), "r");
wchar_t c;
if (f == NULL) {
printf("fmemopen failed: %s\n", strerror(errno));
return 1;
}
printf("default wide orientation: %d\n", fwide(f, 0));
printf("selected wide orientation: %d\n", fwide(f, 1));
while ((c = getwc(f)) != WEOF) {
printf("read %lc (%d 0x%x)\n", c, c, c);
}
return 0;
}
Run on linux:
default wide orientation: -1
selected wide orientation: -1
No output, WEOF
is returned immediately.
Explanation for fwide(f, 0)
from the linux man page:
SYNOPSIS
#include <wchar.h> int fwide(FILE *stream, int mode);
When
mode
is zero, thefwide()
function determines the current orientation ofstream
. It returns a positive value ifstream
is wide-character oriented, that is, if wide-character I/O is permitted but char I/O is disallowed. It returns a negative value ifstream
is byte oriented, i.e., if char I/O is permitted but wide-character I/O is disallowed. It returns zero ifstream
has no orientation yet; in this case the next I/O operation might change the orientation (to byte oriented if it is a char I/O operation, or to wide-character oriented if it is a wide-character I/O operation).Once a stream has an orientation, it cannot be changed and persists until the stream is closed.
When
mode
is nonzero, thefwide()
function first attempts to setstream
's orientation (to wide-character oriented if mode is greater than 0, or to byte oriented ifmode
is less than 0). It then returns a value denoting the current orientation, as above.
The stream returned by fmemopen()
is byte-oriented and cannot be changed to wide-character oriented.
Your second line does not use the correct number of parameters, does it? corrected
FILE *fmemopen(void *buf, size_t size, const char *mode);
glibc's fmemopen
does not (fully) support wide characters AFAIK. There's also open_wmemstream()
, which supports wide characters but is just for writing.
Is _UNICODE
defined? See wchar_t reading.
Also, have you set the locale to an encoding that supports Unicode, for example, setlocale(LC_ALL, "en_US.UTF-8");
? See here.
Consider using a temporary file. Consider using fgetwc / 4 instead.
I have changed my code and adopted the code from @chqrlie since it more close to the OP code but added the locale, otherwise it fails to produce correct output for extended/Unicode characters.
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <stdlib.h>
#include <locale.h>
int main(void)
{
setlocale(LC_ALL, "en_US.UTF-8");
const char *s = "foo $€ bar foo";
FILE *f = fmemopen((void *)s, strlen(s), "r");
wchar_t c;
if (f == NULL) {
printf("fmemopen failed: %s\n", strerror(errno));
return 1;
}
printf("default wide orientation: %d\n", fwide(f, 0));
printf("selected wide orientation: %d\n", fwide(f, 1));
while ((c = getwc(f)) != WEOF) {
printf("read %lc (%d 0x%x)\n", c, c, c);
}
return 0;
}
You can use getwc()
only on unoriented or wide-oriented stream. From getwc()
man page: The stream shall not have an orientation yet, or be wide-oriented.
It is not possible to change stream orientation, if the stream already has orientation. From fwide()
man page: Calling this function on a stream that already has an orientation cannot change it.
Stream opened with glibc's fmemopen()
has an byte-orientation and therefore can't be wide-oriented in any way. As described here uClibc has fmemopen()
routine without this limitation.
Conclusion: You need to use uClibc or another library or make your own fmemopen()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With