aboutsummaryrefslogtreecommitdiff
path: root/lib/mlibc/tests/posix/fmemopen.c
blob: 4d5046eb00497d4f86ff57a6af37d41758225630 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BUFFER0_SIZE 0x1000
#define BUFFER1 "Hello world"
#define BUFFER1_SIZE (sizeof(BUFFER1))

int main() {
	// test seek with mode "r"
	FILE *f = fmemopen(NULL, BUFFER0_SIZE, "r");
	assert(f);

	int ret = fseek(f, 0, SEEK_END);
	assert(!ret);
	long pos = ftell(f);
	fprintf(stderr, "pos = %ld\n", pos);
	// Despite the fact that this is correct behavior (see below),
	// this sometimes fails on glibc; newlib seems to get this wrong, too.
	// to quote what posix says about it:
	// > The stream shall also maintain the size of the current buffer contents;
	// > use of fseek() or fseeko() on the stream with SEEK_END shall seek relative to this size.
	// > For modes r and r+ the size shall be set to the value given by the size argument.
#if !defined(__GLIBC__)
	assert(pos == BUFFER0_SIZE);
#endif

	fclose(f);

	// test seek with mode "w"
	f = fmemopen(NULL, BUFFER0_SIZE, "w");
	assert(f);

	ret = fseek(f, 0, SEEK_END);
	assert(!ret);
	pos = ftell(f);
	assert(pos == 0);

	fclose(f);

	// test seek with mode "a" and a NULL buffer
	f = fmemopen(NULL, BUFFER0_SIZE, "a");
	assert(f);

	ret = fseek(f, 0, SEEK_END);
	assert(!ret);
	pos = ftell(f);
	assert(pos == 0);

	fclose(f);

	// test seek with mode "a" and a buffer containing a '\0'
	f = fmemopen(BUFFER1, BUFFER1_SIZE + 2, "a");
	assert(f);

	pos = ftell(f);
	assert(pos == (long) (BUFFER1_SIZE - 1));

	ret = fseek(f, 0, SEEK_SET);
	assert(!ret);
	pos = ftell(f);
	assert(!pos);

	ret = fseek(f, 0, SEEK_END);
	assert(!ret);
	pos = ftell(f);
	assert(pos == (long) (BUFFER1_SIZE - 1));

	fclose(f);

	// test seek with mode "a" and a buffer not containing a '\0'
	f = fmemopen(BUFFER1, BUFFER1_SIZE - 2, "a");
	assert(f);

	ret = fseek(f, 0, SEEK_END);
	assert(!ret);
	pos = ftell(f);
	assert(pos == (long) (BUFFER1_SIZE - 2));

	fclose(f);

	f = fmemopen(BUFFER1, BUFFER1_SIZE, "r");
	assert(f);

	ret = fseek(f, 0, SEEK_SET);
	assert(!ret);

	char buf[BUFFER1_SIZE];
	int read = fread(buf, 1, BUFFER1_SIZE - 2, f);
	assert(read == BUFFER1_SIZE - 2);
	assert(!strncmp(BUFFER1, buf, BUFFER1_SIZE - 2));

	fseek(f, 0, SEEK_END);

	read = fread(buf, 1, 2, f);
	assert(read == 0);
	assert(feof(f));

	fclose(f);

	// Open a buffer for read+write
	char *buf1 = strdup(BUFFER1);
	f = fmemopen(buf1, BUFFER1_SIZE, "w+");
	assert(f);
	assert(strlen(BUFFER1) == BUFFER1_SIZE - 1);

	// seek to somewhere in the middle of the buffer
	fseek(f, BUFFER1_SIZE - 5, SEEK_SET);
	// write as much data to it as possible
	read = fwrite(BUFFER1, 1, 9, f);
	rewind(f);

	// seek the the same position in the middle of the buffer
	ret = fseek(f, BUFFER1_SIZE - 5, SEEK_SET);
	assert(!ret);
	memset(buf, 0, BUFFER1_SIZE);
	// read what we just wrote
	read = fread(buf, 1, 5, f);
	// check that the write got correctly truncated
	fprintf(stderr, "buf '%s' (%zu)\n", buf, strlen(buf));
	assert(!strncmp(BUFFER1, buf, 4) && strlen(buf) == 4);

	fclose(f);
	free(buf1);

	char *buf2 = strdup(BUFFER1);
	f = fmemopen(buf2, 0, "r");
	assert(f || errno == EINVAL);

	if(f) {
		memset(buf, 0, BUFFER1_SIZE);
		read = fread(buf, 10, 1, f);
		assert(!read);
		rewind(f);

		read = fwrite(BUFFER1, 1, 12, f);
		assert(read == 0);

		fclose(f);
	}
	free(buf2);

	return 0;
}