|
1 /************************************************************************ |
|
2 * |
|
3 * file.cpp - definitions of testsuite file I/O helpers |
|
4 * |
|
5 * $Id: file.cpp 290020 2005-09-18 23:58:30Z sebor $ |
|
6 * |
|
7 ************************************************************************ |
|
8 * |
|
9 * Copyright (c) 1994-2005 Quovadx, Inc., acting through its Rogue Wave |
|
10 * Software division. Licensed under the Apache License, Version 2.0 (the |
|
11 * "License"); you may not use this file except in compliance with the |
|
12 * License. You may obtain a copy of the License at |
|
13 * http://www.apache.org/licenses/LICENSE-2.0. Unless required by |
|
14 * applicable law or agreed to in writing, software distributed under |
|
15 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR |
|
16 * CONDITIONS OF ANY KIND, either express or implied. See the License |
|
17 * for the specific language governing permissions and limitations under |
|
18 * the License. |
|
19 * |
|
20 **************************************************************************/ |
|
21 |
|
22 // expand _TEST_EXPORT macros |
|
23 #define _RWSTD_TEST_SRC |
|
24 #include <testdefs.h> |
|
25 #include <file.h> |
|
26 |
|
27 #if defined __linux__ |
|
28 // on Linux define _XOPEN_SOURCE to get CODESET defined in <langinfo.h> |
|
29 # define _XOPEN_SOURCE 500 /* Single Unix conformance */ |
|
30 // bring __int32_t into scope (otherwise <wctype.h> fails to compile) |
|
31 # include <sys/types.h> |
|
32 #endif // __linux__ |
|
33 |
|
34 #include <fcntl.h> |
|
35 #include <sys/stat.h> |
|
36 |
|
37 #if (!defined (_WIN32) && !defined (_WIN64)) || defined (__SYMBIAN32__) |
|
38 # include <langinfo.h> // for CODESET |
|
39 # include <unistd.h> |
|
40 #else |
|
41 # include <io.h> |
|
42 #endif |
|
43 |
|
44 #include <assert.h> // for assert |
|
45 #include <errno.h> // for errno |
|
46 #include <locale.h> // for LC_XXX macros |
|
47 #include <stdio.h> // for sprintf, ... |
|
48 #include <stdlib.h> // for free, malloc, realloc |
|
49 #include <string.h> // for strcat, strcpy, strlen, ... |
|
50 #include <ctype.h> |
|
51 #include <wchar.h> // for wcslen, ... |
|
52 |
|
53 |
|
54 #ifndef PATH_MAX |
|
55 # define PATH_MAX 1024 |
|
56 #endif |
|
57 |
|
58 #ifndef P_tmpdir |
|
59 # define P_tmpdir "/tmp/" |
|
60 #endif |
|
61 |
|
62 #ifndef _RWSTD_NO_PURE_C_HEADERS |
|
63 |
|
64 extern "C" int mkstemp (char*); |
|
65 |
|
66 #endif // _RWSTD_NO_PURE_C_HEADERS |
|
67 |
|
68 // write `str' using symbolic names from the Portable Character Set (PCS) |
|
69 // or using the <U00XX> notations for narrow characters outside that set |
|
70 // if (0 == str), writes out the CHARMAP section of the locale definition |
|
71 // file for the Portable Character Set (in POSIX-compliant format) |
|
72 _TEST_EXPORT void pcs_write (void *fpv, const char *str) |
|
73 { |
|
74 FILE* const fp = _RWSTD_STATIC_CAST (FILE*, fpv); |
|
75 |
|
76 // ASCII (ISO-646) character map definition |
|
77 static const char* charmap[] = { |
|
78 "<NUL>", "<SOH>", "<STX>", "<ETX>", "<EOT>", "<ENQ>", "<ACK>", "<BEL>", |
|
79 "<backspace>", |
|
80 "<tab>", |
|
81 "<newline>", |
|
82 "<vertical-tab>", |
|
83 "<form-feed>", |
|
84 "<carriage-return>", |
|
85 "<SO>", "<SI>", "<DLE>", "<DC1>", "<DC2>", "<DC3>", "<DC4>", "<NAK>", |
|
86 "<SYN>","<ETB>", "<CAN>", "<EM>", "<SUB>", "<ESC>", "<IS4>", "<IS3>", |
|
87 "<IS2>", "<IS1>", |
|
88 "<space>", |
|
89 /* ! */ "<exclamation-mark>", |
|
90 /* " */ "<quotation-mark>", |
|
91 /* # */ "<number-sign>", |
|
92 /* $ */ "<dollar-sign>", |
|
93 /* % */ "<percent-sign>", |
|
94 /* & */ "<ampersand>", |
|
95 /* ' */ "<apostrophe>", |
|
96 /* ( */ "<left-parenthesis>", |
|
97 /* ) */ "<right-parenthesis>", |
|
98 /* * */ "<asterisk>", |
|
99 /* + */ "<plus-sign>", |
|
100 /* , */ "<comma>", |
|
101 /* - */ "<hyphen>", |
|
102 /* . */ "<period>", |
|
103 /* / */ "<slash>", |
|
104 /* 0 */ "<zero>", |
|
105 /* 1 */ "<one>", |
|
106 /* 2 */ "<two>", |
|
107 /* 3 */ "<three>", |
|
108 /* 4 */ "<four>", |
|
109 /* 5 */ "<five>", |
|
110 /* 6 */ "<six>", |
|
111 /* 7 */ "<seven>", |
|
112 /* 8 */ "<eight>", |
|
113 /* 9 */ "<nine>", |
|
114 /* : */ "<colon>", |
|
115 /* ; */ "<semicolon>", |
|
116 /* < */ "<less-than-sign>", |
|
117 /* = */ "<equals-sign>", |
|
118 /* > */ "<greater-than-sign>", |
|
119 /* ? */ "<question-mark>", |
|
120 /* @ */ "<commercial-at>", |
|
121 "<A>", "<B>", "<C>", "<D>", "<E>", "<F>", "<G>", "<H>", "<I>", "<J>", |
|
122 "<K>", "<L>", "<M>", "<N>", "<O>", "<P>", "<Q>", "<R>", "<S>", "<T>", |
|
123 "<U>", "<V>", "<W>", "<X>", "<Y>", "<Z>", |
|
124 /* [ */ "<left-square-bracket>", |
|
125 /* \ */ "<backslash>", |
|
126 /* ] */ "<right-square-bracket>", |
|
127 /* ^ */ "<circumflex>", |
|
128 /* _ */ "<underscore>", |
|
129 /* ` */ "<grave-accent>", |
|
130 "<a>", "<b>", "<c>", "<d>", "<e>", "<f>", "<g>", "<h>", "<i>", "<j>", |
|
131 "<k>", "<l>", "<m>", "<n>", "<o>", "<p>", "<q>", "<r>", "<s>", "<t>", |
|
132 "<u>", "<v>", "<w>", "<x>", "<y>", "<z>", |
|
133 /* { */ "<left-brace>", |
|
134 /* | */ "<vertical-line>", |
|
135 /* } */ "<right-brace>", |
|
136 /* ~ */ "<tilde>", |
|
137 "<DEL>" |
|
138 }; |
|
139 |
|
140 if (str) { |
|
141 // write out `str' using the charmap above |
|
142 for (; *str; ++str) { |
|
143 const unsigned char uc = _RWSTD_STATIC_CAST (unsigned char, *str); |
|
144 |
|
145 if (uc < sizeof charmap / sizeof *charmap) |
|
146 fprintf (fp, "%s", charmap [uc]); |
|
147 else |
|
148 fprintf (fp, "<U%04X>", uc); |
|
149 } |
|
150 } |
|
151 else { |
|
152 |
|
153 #if !defined (_WIN32) && !defined (_WIN64) |
|
154 const char* const codeset = nl_langinfo (CODESET); |
|
155 #else |
|
156 // FIXME: determine the current code page |
|
157 const char* const codeset = "UTF-8"; |
|
158 #endif // _WIN{32,64} |
|
159 |
|
160 fprintf (fp, "<code_set_name> \"%s\"\n", codeset); |
|
161 fprintf (fp, "<mb_cur_max> 1\n"); |
|
162 fprintf (fp, "<mb_cur_min> 1\n"); |
|
163 |
|
164 fprintf (fp, "CHARMAP\n"); |
|
165 |
|
166 // write out the charmap above |
|
167 for (unsigned i = 0; i != sizeof charmap / sizeof *charmap; ++i) { |
|
168 fprintf (fp, "%s \\x%02x\n", charmap [i], i); |
|
169 } |
|
170 |
|
171 // write out duplicate symbolic names to prevent warnings |
|
172 fprintf (fp, "<alert> \\x%02x\n", '\a'); |
|
173 fprintf (fp, "<hyphen-minus> \\x%02x\n", '-'); |
|
174 fprintf (fp, "<full-stop> \\x%02x\n", '.'); |
|
175 fprintf (fp, "<solidus> \\x%02x\n", '/'); |
|
176 fprintf (fp, "<reverse-solidus> \\x%02x\n", '\\'); |
|
177 fprintf (fp, "<circumflex-accent> \\x%02x\n", '^'); |
|
178 fprintf (fp, "<underline> \\x%02x\n", '_'); |
|
179 fprintf (fp, "<low-line> \\x%02x\n", '_'); |
|
180 fprintf (fp, "<left-curly-bracket> \\x%02x\n", '{'); |
|
181 fprintf (fp, "<right-curly-bracket> \\x%02x\n", '}'); |
|
182 |
|
183 fprintf (fp, "END CHARMAP\n"); |
|
184 } |
|
185 } |
|
186 |
|
187 |
|
188 _TEST_EXPORT |
|
189 const char* rw_tmpnam (char *buf) |
|
190 { |
|
191 #ifndef _RWSTD_NO_MKSTEMP |
|
192 # define TMP_TEMPLATE "tmpfile-XXXXXX" |
|
193 |
|
194 if (!buf) { |
|
195 static char fname_buf [sizeof (P_tmpdir) + sizeof (TMP_TEMPLATE)]; |
|
196 |
|
197 buf = fname_buf; |
|
198 *buf = '\0'; |
|
199 } |
|
200 |
|
201 if ('\0' == *buf) { |
|
202 // copy the template to the buffer; make sure there is exactly |
|
203 // one path separator character between P_tmpdir and the file |
|
204 // name template (it doesn't really matter how many there are |
|
205 // as long as it's at least one, but one looks better than two |
|
206 // in diagnostic messages) |
|
207 size_t len = sizeof (P_tmpdir) - 1; |
|
208 |
|
209 memcpy (buf, P_tmpdir, len); |
|
210 if (_RWSTD_PATH_SEP != buf [len - 1]) |
|
211 buf [len++] = _RWSTD_PATH_SEP; |
|
212 |
|
213 memcpy (buf + len, TMP_TEMPLATE, sizeof TMP_TEMPLATE); |
|
214 } |
|
215 |
|
216 // prevent annoying glibc warnings (issued by the linker): |
|
217 // the use of `tmpnam' is dangerous, better use `mkstemp' |
|
218 |
|
219 const int fd = mkstemp (buf); |
|
220 |
|
221 if (-1 == fd) { |
|
222 fprintf (stderr, "%s:%d: mkstemp(\"%s\") failed: %s\n", |
|
223 __FILE__, __LINE__, buf, strerror (errno)); |
|
224 return 0; |
|
225 } |
|
226 |
|
227 close (fd); |
|
228 |
|
229 const char* const fname = buf; |
|
230 |
|
231 # undef TMP_TEMPLATE |
|
232 #else // if defined (_RWSTD_NO_MKSTEMP) |
|
233 |
|
234 # if defined (_WIN32) || defined (_WIN64) |
|
235 |
|
236 // create a temporary file name |
|
237 char* fname = tempnam (P_tmpdir, ".rwtest-tmp"); |
|
238 |
|
239 if (fname) { |
|
240 |
|
241 static char tmpbuf [256]; |
|
242 |
|
243 if (0 == buf) |
|
244 buf = tmpbuf; |
|
245 |
|
246 _RWSTD_ASSERT (strlen (fname) < sizeof (tmpbuf)); |
|
247 |
|
248 // copy the generated temporary file name to the provided buffer |
|
249 strcpy (buf, fname); |
|
250 |
|
251 // free the storage allocated by tempnam() |
|
252 free (fname); |
|
253 fname = buf; |
|
254 } |
|
255 else { |
|
256 fprintf (stderr, "%s:%d: tempnam(\"%s\", \"%s\") failed: %s\n", |
|
257 __FILE__, __LINE__, |
|
258 P_tmpdir, ".rwtest-tmp", strerror (errno)); |
|
259 } |
|
260 |
|
261 # else |
|
262 # if defined (__hpux) && defined (_RWSTD_REENTRANT) |
|
263 |
|
264 // on HP-UX, in reentrant mode, tmpnam(0) fails by design |
|
265 |
|
266 if (!buf) { |
|
267 static char tmpbuf [L_tmpnam]; |
|
268 buf = tmpbuf; |
|
269 *buf = '\0'; |
|
270 } |
|
271 |
|
272 # endif // __hpux && _REENTRANT |
|
273 |
|
274 const char* const fname = tmpnam (buf); |
|
275 |
|
276 if (!fname) |
|
277 fprintf (stderr, "%s:%d: tmpnam(\"%s\") failed: %s\n", |
|
278 __FILE__, __LINE__, buf, strerror (errno)); |
|
279 |
|
280 # endif // _WIN{32,64} |
|
281 #endif // _RWSTD_NO_MKSTEMP |
|
282 |
|
283 return fname; |
|
284 } |
|
285 |
|
286 |
|
287 _TEST_EXPORT |
|
288 size_t rw_fsize (const char *fname) |
|
289 { |
|
290 #ifdef __SYMBIAN32__ |
|
291 |
|
292 struct stat sb; |
|
293 |
|
294 if (-1 == stat (fname, &sb)) |
|
295 return _RWSTD_SIZE_MAX; |
|
296 |
|
297 return sb.st_size; |
|
298 #elif defined (_WIN32) || defined (_WIN64) |
|
299 |
|
300 // note: both method of obtaining the size of a file |
|
301 // just written by a process may fail (i.e., the size |
|
302 // will be 0) |
|
303 |
|
304 # if 1 |
|
305 |
|
306 struct _stat sb; |
|
307 |
|
308 if (-1 == _stat (fname, &sb)) |
|
309 return _RWSTD_SIZE_MAX; |
|
310 |
|
311 return sb.st_size; |
|
312 |
|
313 # else |
|
314 |
|
315 // #include <windows.h> for CreateFile() and GetFileSize() |
|
316 const HANDLE hfile = |
|
317 CreateFile (fname, |
|
318 GENERIC_READ, |
|
319 0, // dwShareMode, |
|
320 0, // lpSecurityAttributes, |
|
321 OPEN_EXISTING, // dwCreationDisposition, |
|
322 FILE_ATTRIBUTE_NORMAL, // dwFlagsAndAttributes, |
|
323 0); // hTemplateFile |
|
324 |
|
325 if (INVALID_HANDLE_VALUE == hfile) |
|
326 return _RWSTD_SIZE_MAX; |
|
327 |
|
328 const size_t size = GetFileSize (hfile, 0); |
|
329 |
|
330 CloseHandle (hfile); |
|
331 |
|
332 return size; |
|
333 |
|
334 # endif // 0/1 |
|
335 |
|
336 #else // if !defined (_WIN{32,64}) |
|
337 |
|
338 struct stat sb; |
|
339 |
|
340 if (stat (fname, &sb) == -1) |
|
341 return _RWSTD_SIZE_MAX; |
|
342 |
|
343 return sb.st_size; |
|
344 |
|
345 #endif // _WIN{32,64} |
|
346 |
|
347 } |
|
348 |
|
349 |
|
350 _TEST_EXPORT |
|
351 void* rw_fread (const char *fname, |
|
352 size_t *size /* = 0 */, |
|
353 const char *mode /* = "r" */) |
|
354 { |
|
355 // buffer and size supplied by the user |
|
356 static char* usrbuf = 0; |
|
357 static size_t usrsize = 0; |
|
358 |
|
359 // when called with 0 file name and non-0 size, set the static |
|
360 // local buffer for the functions to use in subsequent calls |
|
361 // with non-0 `fname' instead of dynamically allocating a new |
|
362 // buffer |
|
363 if (!fname && size) { |
|
364 |
|
365 char* const oldbuf = usrbuf; |
|
366 |
|
367 usrbuf = _RWSTD_CONST_CAST (char*, mode); |
|
368 usrsize = usrbuf ? *size : 0; |
|
369 |
|
370 return oldbuf; |
|
371 } |
|
372 |
|
373 static char buffer [1024]; |
|
374 static char* buf = usrbuf ? usrbuf : buffer; |
|
375 static size_t bufsize = usrbuf ? usrsize : sizeof buffer; |
|
376 |
|
377 // open the file in the specified mode |
|
378 FILE* const fp = fopen (fname, mode); |
|
379 |
|
380 if (!fp) |
|
381 return 0; |
|
382 |
|
383 for (char *bufend = buf; ; ) { |
|
384 // compute the total number of bytes read from the file so far |
|
385 // and the number of bytes that are still available in the buffer |
|
386 const size_t bytes_read = size_t (bufend - buf); |
|
387 const size_t bytes_avail = bufsize - bytes_read; |
|
388 |
|
389 // try to read the contents of the file into the buffer |
|
390 const size_t nbytes = fread (bufend, 1, bytes_avail, fp); |
|
391 |
|
392 if (0 == nbytes) { |
|
393 *bufend = '\0'; |
|
394 |
|
395 // store the number of bytes read |
|
396 if (size) |
|
397 *size = bytes_read; |
|
398 |
|
399 break; |
|
400 } |
|
401 |
|
402 if (nbytes == bytes_avail) { |
|
403 |
|
404 // do not grow user-specified buffer |
|
405 if (buf == usrbuf) |
|
406 break; |
|
407 |
|
408 const size_t newsize = (bufsize + 1) * 2; |
|
409 |
|
410 // increase the size of the buffer and continue reading |
|
411 char *tmp = new char [newsize]; |
|
412 memcpy (tmp, buf, bufsize); |
|
413 |
|
414 // deallocate buffer only if it's been |
|
415 // previously dynamically allocated |
|
416 if (buf != buffer) |
|
417 delete[] buf; |
|
418 |
|
419 bufsize = newsize; |
|
420 bufend = tmp + bytes_read; |
|
421 buf = tmp; |
|
422 } |
|
423 |
|
424 bufend += nbytes; |
|
425 } |
|
426 |
|
427 fclose (fp); |
|
428 |
|
429 return buf; |
|
430 } |
|
431 |
|
432 |
|
433 _TEST_EXPORT |
|
434 size_t rw_fwrite (const char *fname, |
|
435 const void *buf, |
|
436 size_t size /* = -1 */, |
|
437 const char *mode /* = "w" */) |
|
438 { |
|
439 FILE *fp = 0; |
|
440 |
|
441 if (buf) |
|
442 fp = fopen (fname, mode); |
|
443 else { |
|
444 remove (fname); |
|
445 return 0; |
|
446 } |
|
447 |
|
448 if (!fp) |
|
449 return size_t (-1); |
|
450 |
|
451 if (size_t (-1) == size) |
|
452 size = strlen (_RWSTD_STATIC_CAST (const char*, buf)); |
|
453 |
|
454 // fwrite() returns the number of elements successfully written |
|
455 // set it up so that the number of elements == the number of bytes |
|
456 const size_t nbytes = fwrite (buf, 1 /* byte */, size, fp); |
|
457 |
|
458 fclose (fp); |
|
459 |
|
460 return nbytes; |
|
461 } |