1 | /* Verify that ftell returns the correct value after a read and a write on a |
2 | file opened in a+ mode. |
3 | Copyright (C) 2014-2024 Free Software Foundation, Inc. |
4 | This file is part of the GNU C Library. |
5 | |
6 | The GNU C Library is free software; you can redistribute it and/or |
7 | modify it under the terms of the GNU Lesser General Public |
8 | License as published by the Free Software Foundation; either |
9 | version 2.1 of the License, or (at your option) any later version. |
10 | |
11 | The GNU C Library is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | Lesser General Public License for more details. |
15 | |
16 | You should have received a copy of the GNU Lesser General Public |
17 | License along with the GNU C Library; if not, see |
18 | <https://www.gnu.org/licenses/>. */ |
19 | |
20 | #include <stdio.h> |
21 | #include <stdlib.h> |
22 | #include <string.h> |
23 | #include <errno.h> |
24 | #include <unistd.h> |
25 | #include <locale.h> |
26 | #include <wchar.h> |
27 | |
28 | /* data points to either char_data or wide_data, depending on whether we're |
29 | testing regular file mode or wide mode respectively. Similarly, |
30 | fputs_func points to either fputs or fputws. data_len keeps track of the |
31 | length of the current data and file_len maintains the current file |
32 | length. */ |
33 | #define BUF_LEN 4 |
34 | static void *buf; |
35 | static char char_buf[BUF_LEN]; |
36 | static wchar_t wide_buf[BUF_LEN]; |
37 | static const void *data; |
38 | static const char *char_data = "abcdefghijklmnopqrstuvwxyz" ; |
39 | static const wchar_t *wide_data = L"abcdefghijklmnopqrstuvwxyz" ; |
40 | static size_t data_len; |
41 | static size_t file_len; |
42 | |
43 | typedef int (*fputs_func_t) (const void *data, FILE *fp); |
44 | fputs_func_t fputs_func; |
45 | |
46 | typedef void *(*fgets_func_t) (void *s, int size, FILE *stream); |
47 | fgets_func_t fgets_func; |
48 | |
49 | static int do_test (void); |
50 | |
51 | #define TEST_FUNCTION do_test () |
52 | #include "../test-skeleton.c" |
53 | |
54 | static FILE * |
55 | init_file (const char *filename) |
56 | { |
57 | FILE *fp = fopen (filename, "w" ); |
58 | if (fp == NULL) |
59 | { |
60 | printf (format: "fopen: %m\n" ); |
61 | return NULL; |
62 | } |
63 | |
64 | int written = fputs_func (data, fp); |
65 | |
66 | if (written == EOF) |
67 | { |
68 | printf (format: "fputs failed to write data\n" ); |
69 | fclose (fp); |
70 | return NULL; |
71 | } |
72 | |
73 | file_len = data_len; |
74 | |
75 | fclose (fp); |
76 | |
77 | fp = fopen (filename, "a+" ); |
78 | if (fp == NULL) |
79 | { |
80 | printf (format: "fopen(a+): %m\n" ); |
81 | return NULL; |
82 | } |
83 | |
84 | return fp; |
85 | } |
86 | |
87 | static int |
88 | do_one_test (const char *filename) |
89 | { |
90 | FILE *fp = init_file (filename); |
91 | |
92 | if (fp == NULL) |
93 | return 1; |
94 | |
95 | void *ret = fgets_func (buf, BUF_LEN, fp); |
96 | |
97 | if (ret == NULL) |
98 | { |
99 | printf (format: "read failed: %m\n" ); |
100 | fclose (fp); |
101 | return 1; |
102 | } |
103 | |
104 | int written = fputs_func (data, fp); |
105 | |
106 | if (written == EOF) |
107 | { |
108 | printf (format: "fputs failed to write data\n" ); |
109 | fclose (fp); |
110 | return 1; |
111 | } |
112 | |
113 | file_len += data_len; |
114 | |
115 | long off = ftell (stream: fp); |
116 | |
117 | if (off != file_len) |
118 | { |
119 | printf (format: "Incorrect offset %ld, expected %zu\n" , off, file_len); |
120 | fclose (fp); |
121 | return 1; |
122 | } |
123 | else |
124 | printf (format: "Correct offset %ld after write.\n" , off); |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | /* Run the tests for regular files and wide mode files. */ |
130 | static int |
131 | do_test (void) |
132 | { |
133 | int ret = 0; |
134 | char *filename; |
135 | int fd = create_temp_file (base: "tst-ftell-append-tmp." , filename: &filename); |
136 | |
137 | if (fd == -1) |
138 | { |
139 | printf (format: "create_temp_file: %m\n" ); |
140 | return 1; |
141 | } |
142 | |
143 | close (fd: fd); |
144 | |
145 | /* Tests for regular files. */ |
146 | puts (s: "Regular mode:" ); |
147 | fputs_func = (fputs_func_t) fputs; |
148 | fgets_func = (fgets_func_t) fgets; |
149 | data = char_data; |
150 | buf = char_buf; |
151 | data_len = strlen (char_data); |
152 | ret |= do_one_test (filename); |
153 | |
154 | /* Tests for wide files. */ |
155 | puts (s: "Wide mode:" ); |
156 | if (setlocale (LC_ALL, "en_US.UTF-8" ) == NULL) |
157 | { |
158 | printf (format: "Cannot set en_US.UTF-8 locale.\n" ); |
159 | return 1; |
160 | } |
161 | fputs_func = (fputs_func_t) fputws; |
162 | fgets_func = (fgets_func_t) fgetws; |
163 | data = wide_data; |
164 | buf = wide_buf; |
165 | data_len = wcslen (s: wide_data); |
166 | ret |= do_one_test (filename); |
167 | |
168 | return ret; |
169 | } |
170 | |