/src/wxwidgets/src/common/file.cpp
Line | Count | Source |
1 | | ///////////////////////////////////////////////////////////////////////////// |
2 | | // Name: src/common/file.cpp |
3 | | // Purpose: wxFile - encapsulates low-level "file descriptor" |
4 | | // wxTempFile |
5 | | // Author: Vadim Zeitlin |
6 | | // Created: 29/01/98 |
7 | | // Copyright: (c) 1998 Vadim Zeitlin <zeitlin@dptmaths.ens-cachan.fr> |
8 | | // Licence: wxWindows licence |
9 | | ///////////////////////////////////////////////////////////////////////////// |
10 | | |
11 | | // ---------------------------------------------------------------------------- |
12 | | // headers |
13 | | // ---------------------------------------------------------------------------- |
14 | | |
15 | | // For compilers that support precompilation, includes "wx.h". |
16 | | #include "wx/wxprec.h" |
17 | | |
18 | | |
19 | | #if wxUSE_FILE |
20 | | |
21 | | // standard |
22 | | #if defined(__WINDOWS__) && !defined(__GNUWIN32__) |
23 | | |
24 | | #define WIN32_LEAN_AND_MEAN |
25 | | #define NOSERVICE |
26 | | #define NOIME |
27 | | #define NOATOM |
28 | | #define NOGDI |
29 | | #define NOGDICAPMASKS |
30 | | #define NOMETAFILE |
31 | | #define NOMINMAX |
32 | | #define NOMSG |
33 | | #define NOOPENFILE |
34 | | #define NORASTEROPS |
35 | | #define NOSCROLL |
36 | | #define NOSOUND |
37 | | #define NOSYSMETRICS |
38 | | #define NOTEXTMETRIC |
39 | | #define NOWH |
40 | | #define NOCOMM |
41 | | #define NOKANJI |
42 | | #define NOCRYPT |
43 | | #define NOMCX |
44 | | |
45 | | #elif (defined(__UNIX__) || defined(__GNUWIN32__)) |
46 | | #include <unistd.h> |
47 | | #include <time.h> |
48 | | #include <sys/stat.h> |
49 | | #ifdef __GNUWIN32__ |
50 | | #include "wx/msw/wrapwin.h" |
51 | | #endif |
52 | | #else |
53 | | #error "Please specify the header with file functions declarations." |
54 | | #endif //Win/UNIX |
55 | | |
56 | | #include <stdio.h> // SEEK_xxx constants |
57 | | |
58 | | #include <errno.h> |
59 | | |
60 | | // Windows compilers don't have these constants |
61 | | #ifndef W_OK |
62 | | enum |
63 | | { |
64 | | F_OK = 0, // test for existence |
65 | | X_OK = 1, // execute permission |
66 | | W_OK = 2, // write |
67 | | R_OK = 4 // read |
68 | | }; |
69 | | #endif // W_OK |
70 | | |
71 | | // wxWidgets |
72 | | #ifndef WX_PRECOMP |
73 | | #include "wx/string.h" |
74 | | #include "wx/intl.h" |
75 | | #include "wx/log.h" |
76 | | #include "wx/crt.h" |
77 | | #endif // !WX_PRECOMP |
78 | | |
79 | | #include "wx/filename.h" |
80 | | #include "wx/file.h" |
81 | | #include "wx/filefn.h" |
82 | | |
83 | | // there is no distinction between text and binary files under Unix, so define |
84 | | // O_BINARY as 0 if the system headers don't do it already |
85 | | #if defined(__UNIX__) && !defined(O_BINARY) |
86 | 0 | #define O_BINARY (0) |
87 | | #endif //__UNIX__ |
88 | | |
89 | | // ============================================================================ |
90 | | // implementation of wxFile |
91 | | // ============================================================================ |
92 | | |
93 | | // ---------------------------------------------------------------------------- |
94 | | // static functions |
95 | | // ---------------------------------------------------------------------------- |
96 | | |
97 | | bool wxFile::Exists(const wxString& name) |
98 | 0 | { |
99 | 0 | return wxFileExists(name); |
100 | 0 | } |
101 | | |
102 | | bool wxFile::Access(const wxString& name, OpenMode mode) |
103 | 0 | { |
104 | 0 | int how; |
105 | |
|
106 | 0 | switch ( mode ) |
107 | 0 | { |
108 | 0 | default: |
109 | 0 | wxFAIL_MSG(wxT("bad wxFile::Access mode parameter.")); |
110 | 0 | wxFALLTHROUGH; |
111 | |
|
112 | 0 | case read: |
113 | 0 | how = R_OK; |
114 | 0 | break; |
115 | | |
116 | 0 | case write: |
117 | 0 | how = W_OK; |
118 | 0 | break; |
119 | | |
120 | 0 | case read_write: |
121 | 0 | how = R_OK | W_OK; |
122 | 0 | break; |
123 | 0 | } |
124 | | |
125 | 0 | return wxAccess(name, how) == 0; |
126 | 0 | } |
127 | | |
128 | | // ---------------------------------------------------------------------------- |
129 | | // opening/closing |
130 | | // ---------------------------------------------------------------------------- |
131 | | |
132 | | // ctors |
133 | | wxFile::wxFile(const wxString& fileName, OpenMode mode) |
134 | 0 | { |
135 | 0 | m_fd = fd_invalid; |
136 | 0 | m_lasterror = 0; |
137 | |
|
138 | 0 | Open(fileName, mode); |
139 | 0 | } |
140 | | |
141 | | bool wxFile::CheckForError(wxFileOffset rc) const |
142 | 0 | { |
143 | 0 | if ( rc != -1 ) |
144 | 0 | return false; |
145 | | |
146 | 0 | const_cast<wxFile *>(this)->m_lasterror = errno; |
147 | |
|
148 | 0 | return true; |
149 | 0 | } |
150 | | |
151 | | // create the file, fail if it already exists and bOverwrite |
152 | | bool wxFile::Create(const wxString& fileName, bool bOverwrite, int accessMode) |
153 | 0 | { |
154 | | // if bOverwrite we create a new file or truncate the existing one, |
155 | | // otherwise we only create the new file and fail if it already exists |
156 | 0 | int fildes = wxOpen( fileName, |
157 | 0 | O_BINARY | O_WRONLY | O_CREAT | |
158 | 0 | (bOverwrite ? O_TRUNC : O_EXCL), |
159 | 0 | accessMode ); |
160 | 0 | if ( CheckForError(fildes) ) |
161 | 0 | { |
162 | 0 | wxLogSysError(_("can't create file '%s'"), fileName); |
163 | 0 | return false; |
164 | 0 | } |
165 | | |
166 | 0 | Attach(fildes); |
167 | 0 | return true; |
168 | 0 | } |
169 | | |
170 | | // open the file |
171 | | bool wxFile::Open(const wxString& fileName, OpenMode mode, int accessMode) |
172 | 0 | { |
173 | 0 | int flags = O_BINARY; |
174 | |
|
175 | 0 | switch ( mode ) |
176 | 0 | { |
177 | 0 | case read: |
178 | 0 | flags |= O_RDONLY; |
179 | 0 | break; |
180 | | |
181 | 0 | case write_append: |
182 | 0 | if ( wxFile::Exists(fileName) ) |
183 | 0 | { |
184 | 0 | flags |= O_WRONLY | O_APPEND; |
185 | 0 | break; |
186 | 0 | } |
187 | | //else: fall through as write_append is the same as write if the |
188 | | // file doesn't exist |
189 | 0 | wxFALLTHROUGH; |
190 | |
|
191 | 0 | case write: |
192 | 0 | flags |= O_WRONLY | O_CREAT | O_TRUNC; |
193 | 0 | break; |
194 | | |
195 | 0 | case write_excl: |
196 | 0 | flags |= O_WRONLY | O_CREAT | O_EXCL; |
197 | 0 | break; |
198 | | |
199 | 0 | case read_write: |
200 | 0 | flags |= O_RDWR; |
201 | 0 | break; |
202 | 0 | } |
203 | | |
204 | | #ifdef __WINDOWS__ |
205 | | // only read/write bits for "all" are supported by this function under |
206 | | // Windows, and VC++ 8 returns EINVAL if any other bits are used in |
207 | | // accessMode, so clear them as they have at best no effect anyhow |
208 | | accessMode &= wxS_IRUSR | wxS_IWUSR; |
209 | | #endif // __WINDOWS__ |
210 | | |
211 | 0 | int fildes = wxOpen( fileName, flags, accessMode); |
212 | |
|
213 | 0 | if ( CheckForError(fildes) ) |
214 | 0 | { |
215 | 0 | wxLogSysError(_("can't open file '%s'"), fileName); |
216 | 0 | return false; |
217 | 0 | } |
218 | | |
219 | 0 | Attach(fildes); |
220 | 0 | return true; |
221 | 0 | } |
222 | | |
223 | | // close |
224 | | bool wxFile::Close() |
225 | 0 | { |
226 | 0 | if ( IsOpened() ) { |
227 | 0 | if ( CheckForError(wxClose(m_fd)) ) |
228 | 0 | { |
229 | 0 | wxLogSysError(_("can't close file descriptor %d"), m_fd); |
230 | 0 | m_fd = fd_invalid; |
231 | 0 | return false; |
232 | 0 | } |
233 | 0 | else |
234 | 0 | m_fd = fd_invalid; |
235 | 0 | } |
236 | | |
237 | 0 | return true; |
238 | 0 | } |
239 | | |
240 | | // ---------------------------------------------------------------------------- |
241 | | // read/write |
242 | | // ---------------------------------------------------------------------------- |
243 | | |
244 | | bool wxFile::ReadAll(wxString *str, const wxMBConv& conv) |
245 | 0 | { |
246 | 0 | wxCHECK_MSG( str, false, wxS("Output string must be non-null") ); |
247 | | |
248 | 0 | static const ssize_t READSIZE = 4096; |
249 | |
|
250 | 0 | wxCharBuffer buf; |
251 | |
|
252 | 0 | ssize_t length = Length(); |
253 | 0 | if ( length != -1 ) |
254 | 0 | { |
255 | 0 | wxCHECK_MSG( (wxFileOffset)length == Length(), false, wxT("huge file not supported") ); |
256 | | |
257 | 0 | if ( !buf.extend(length) ) |
258 | 0 | return false; |
259 | | |
260 | 0 | char* p = buf.data(); |
261 | 0 | for ( ;; ) |
262 | 0 | { |
263 | 0 | ssize_t nread = Read(p, length > READSIZE ? READSIZE : length); |
264 | 0 | if ( nread == wxInvalidOffset ) |
265 | 0 | return false; |
266 | | |
267 | 0 | if ( nread == 0 ) |
268 | 0 | { |
269 | | // We have reached EOF before reading the entire length of the |
270 | | // file. This can happen for some special files (e.g. those |
271 | | // under /sys on Linux systems) or even for regular files if |
272 | | // another process has truncated the file since we started |
273 | | // reading it, so deal with it gracefully. |
274 | 0 | buf.shrink(p - buf.data()); |
275 | 0 | break; |
276 | 0 | } |
277 | | |
278 | 0 | p += nread; |
279 | 0 | length -= nread; |
280 | |
|
281 | 0 | if ( !length ) |
282 | 0 | { |
283 | | // Notice that we don't keep reading after getting the expected |
284 | | // number of bytes, even though in principle a situation |
285 | | // similar to the one described above, with another process |
286 | | // extending the file since we started to read it, is possible. |
287 | | // But returning just the data that was in the file when we |
288 | | // originally started reading it isn't really wrong in this |
289 | | // case, so keep things simple and just do it like this. |
290 | 0 | break; |
291 | 0 | } |
292 | 0 | } |
293 | 0 | } |
294 | 0 | else // File is not seekable |
295 | 0 | { |
296 | 0 | for ( ;; ) |
297 | 0 | { |
298 | 0 | const size_t len = buf.length(); |
299 | 0 | if ( !buf.extend(len + READSIZE) ) |
300 | 0 | return false; |
301 | | |
302 | 0 | ssize_t nread = Read(buf.data() + len, READSIZE); |
303 | 0 | if ( nread == wxInvalidOffset ) |
304 | 0 | return false; |
305 | | |
306 | 0 | if ( nread < READSIZE ) |
307 | 0 | { |
308 | | // We have reached EOF. |
309 | 0 | buf.shrink(len + nread); |
310 | 0 | break; |
311 | 0 | } |
312 | 0 | } |
313 | 0 | } |
314 | | |
315 | 0 | str->assign(buf, conv); |
316 | |
|
317 | 0 | return true; |
318 | 0 | } |
319 | | |
320 | | // read |
321 | | ssize_t wxFile::Read(void *pBuf, size_t nCount) |
322 | 0 | { |
323 | 0 | if ( !nCount ) |
324 | 0 | return 0; |
325 | | |
326 | 0 | wxCHECK( (pBuf != nullptr) && IsOpened(), 0 ); |
327 | | |
328 | 0 | ssize_t iRc = wxRead(m_fd, pBuf, nCount); |
329 | |
|
330 | 0 | if ( CheckForError(iRc) ) |
331 | 0 | { |
332 | 0 | wxLogSysError(_("can't read from file descriptor %d"), m_fd); |
333 | 0 | return wxInvalidOffset; |
334 | 0 | } |
335 | | |
336 | 0 | return iRc; |
337 | 0 | } |
338 | | |
339 | | // write |
340 | | size_t wxFile::Write(const void *pBuf, size_t nCount) |
341 | 0 | { |
342 | 0 | if ( !nCount ) |
343 | 0 | return 0; |
344 | | |
345 | 0 | wxCHECK( (pBuf != nullptr) && IsOpened(), 0 ); |
346 | | |
347 | 0 | ssize_t iRc = wxWrite(m_fd, pBuf, nCount); |
348 | |
|
349 | 0 | if ( CheckForError(iRc) ) |
350 | 0 | { |
351 | 0 | wxLogSysError(_("can't write to file descriptor %d"), m_fd); |
352 | 0 | iRc = 0; |
353 | 0 | } |
354 | |
|
355 | 0 | return iRc; |
356 | 0 | } |
357 | | |
358 | | bool wxFile::Write(const wxString& s, const wxMBConv& conv) |
359 | 0 | { |
360 | | // Writing nothing always succeeds -- and simplifies the check for |
361 | | // conversion failure below. |
362 | 0 | if ( s.empty() ) |
363 | 0 | return true; |
364 | | |
365 | 0 | const wxWX2MBbuf buf = s.mb_str(conv); |
366 | |
|
367 | 0 | const size_t size = buf.length(); |
368 | |
|
369 | 0 | if ( !size ) |
370 | 0 | { |
371 | | // This means that the conversion failed as the original string wasn't |
372 | | // empty (we explicitly checked for this above) and in this case we |
373 | | // must fail too to indicate that we can't save the data. |
374 | 0 | return false; |
375 | 0 | } |
376 | | |
377 | 0 | return Write(buf, size) == size; |
378 | 0 | } |
379 | | |
380 | | // flush |
381 | | bool wxFile::Flush() |
382 | 0 | { |
383 | 0 | #ifdef HAVE_FSYNC |
384 | | // fsync() only works on disk files and returns errors for pipes, don't |
385 | | // call it then |
386 | 0 | if ( IsOpened() && GetKind() == wxFILE_KIND_DISK ) |
387 | 0 | { |
388 | 0 | if ( CheckForError(wxFsync(m_fd)) ) |
389 | 0 | { |
390 | 0 | wxLogSysError(_("can't flush file descriptor %d"), m_fd); |
391 | 0 | return false; |
392 | 0 | } |
393 | 0 | } |
394 | 0 | #endif // HAVE_FSYNC |
395 | | |
396 | 0 | return true; |
397 | 0 | } |
398 | | |
399 | | // ---------------------------------------------------------------------------- |
400 | | // seek |
401 | | // ---------------------------------------------------------------------------- |
402 | | |
403 | | // seek |
404 | | wxFileOffset wxFile::Seek(wxFileOffset ofs, wxSeekMode mode) |
405 | 0 | { |
406 | 0 | wxASSERT_MSG( IsOpened(), wxT("can't seek on closed file") ); |
407 | 0 | wxCHECK_MSG( ofs != wxInvalidOffset || mode != wxFromStart, |
408 | 0 | wxInvalidOffset, |
409 | 0 | wxT("invalid absolute file offset") ); |
410 | | |
411 | 0 | int origin; |
412 | 0 | switch ( mode ) { |
413 | 0 | default: |
414 | 0 | wxFAIL_MSG(wxT("unknown seek origin")); |
415 | 0 | wxFALLTHROUGH; |
416 | 0 | case wxFromStart: |
417 | 0 | origin = SEEK_SET; |
418 | 0 | break; |
419 | | |
420 | 0 | case wxFromCurrent: |
421 | 0 | origin = SEEK_CUR; |
422 | 0 | break; |
423 | | |
424 | 0 | case wxFromEnd: |
425 | 0 | origin = SEEK_END; |
426 | 0 | break; |
427 | 0 | } |
428 | | |
429 | 0 | wxFileOffset iRc = wxSeek(m_fd, ofs, origin); |
430 | 0 | if ( CheckForError(iRc) ) |
431 | 0 | { |
432 | 0 | wxLogSysError(_("can't seek on file descriptor %d"), m_fd); |
433 | 0 | } |
434 | |
|
435 | 0 | return iRc; |
436 | 0 | } |
437 | | |
438 | | // get current file offset |
439 | | wxFileOffset wxFile::Tell() const |
440 | 0 | { |
441 | 0 | wxASSERT( IsOpened() ); |
442 | |
|
443 | 0 | wxFileOffset iRc = wxTell(m_fd); |
444 | 0 | if ( CheckForError(iRc) ) |
445 | 0 | { |
446 | 0 | wxLogSysError(_("can't get seek position on file descriptor %d"), m_fd); |
447 | 0 | } |
448 | |
|
449 | 0 | return iRc; |
450 | 0 | } |
451 | | |
452 | | // get current file length |
453 | | wxFileOffset wxFile::Length() const |
454 | 0 | { |
455 | 0 | wxASSERT( IsOpened() ); |
456 | |
|
457 | 0 | wxFileOffset iRc = Tell(); |
458 | 0 | if ( iRc != wxInvalidOffset ) { |
459 | 0 | wxFileOffset iLen = const_cast<wxFile *>(this)->SeekEnd(); |
460 | 0 | if ( iLen != wxInvalidOffset ) { |
461 | | // restore old position |
462 | 0 | if (const_cast<wxFile*>(this)->Seek(iRc) == wxInvalidOffset) |
463 | 0 | { |
464 | | // error |
465 | 0 | iLen = wxInvalidOffset; |
466 | 0 | } |
467 | 0 | } |
468 | |
|
469 | 0 | iRc = iLen; |
470 | 0 | } |
471 | |
|
472 | 0 | if ( iRc == wxInvalidOffset ) |
473 | 0 | { |
474 | | // last error was already set by Tell() |
475 | 0 | wxLogSysError(_("can't find length of file on file descriptor %d"), m_fd); |
476 | 0 | } |
477 | |
|
478 | 0 | return iRc; |
479 | 0 | } |
480 | | |
481 | | // is end of file reached? |
482 | | bool wxFile::Eof() const |
483 | 0 | { |
484 | 0 | wxASSERT( IsOpened() ); |
485 | |
|
486 | 0 | wxFileOffset iRc; |
487 | |
|
488 | 0 | #if defined(__UNIX__) || defined(__GNUWIN32__) |
489 | | // @@ this doesn't work, of course, on unseekable file descriptors |
490 | 0 | wxFileOffset ofsCur = Tell(), |
491 | 0 | ofsMax = Length(); |
492 | 0 | if ( ofsCur == wxInvalidOffset || ofsMax == wxInvalidOffset ) |
493 | 0 | iRc = wxInvalidOffset; |
494 | 0 | else |
495 | 0 | iRc = ofsCur == ofsMax; |
496 | | #else // Windows and "native" compiler |
497 | | iRc = wxEof(m_fd); |
498 | | #endif // Windows/Unix |
499 | |
|
500 | 0 | if ( iRc == 0 ) |
501 | 0 | return false; |
502 | | |
503 | 0 | if ( iRc == wxInvalidOffset ) |
504 | 0 | { |
505 | 0 | wxLogSysError(_("can't determine if the end of file is reached on descriptor %d"), m_fd); |
506 | 0 | } |
507 | |
|
508 | 0 | return true; |
509 | 0 | } |
510 | | |
511 | | // ============================================================================ |
512 | | // implementation of wxTempFile |
513 | | // ============================================================================ |
514 | | |
515 | | // ---------------------------------------------------------------------------- |
516 | | // construction |
517 | | // ---------------------------------------------------------------------------- |
518 | | |
519 | | wxTempFile::wxTempFile(const wxString& strName) |
520 | 0 | { |
521 | 0 | Open(strName); |
522 | 0 | } |
523 | | |
524 | | bool wxTempFile::Open(const wxString& strName) |
525 | 0 | { |
526 | | // we must have an absolute filename because otherwise CreateTempFileName() |
527 | | // would create the temp file in $TMP (i.e. the system standard location |
528 | | // for the temp files) which might be on another volume/drive/mount and |
529 | | // wxRename()ing it later to m_strName from Commit() would then fail |
530 | | // |
531 | | // with the absolute filename, the temp file is created in the same |
532 | | // directory as this one which ensures that wxRename() may work later |
533 | 0 | wxFileName fn(strName); |
534 | 0 | if ( !fn.IsAbsolute() ) |
535 | 0 | { |
536 | 0 | fn.Normalize(wxPATH_NORM_ABSOLUTE); |
537 | 0 | } |
538 | |
|
539 | 0 | m_strName = fn.GetFullPath(); |
540 | |
|
541 | 0 | m_strTemp = wxFileName::CreateTempFileName(m_strName, &m_file); |
542 | |
|
543 | 0 | if ( m_strTemp.empty() ) |
544 | 0 | { |
545 | | // CreateTempFileName() failed |
546 | 0 | return false; |
547 | 0 | } |
548 | | |
549 | 0 | #ifdef __UNIX__ |
550 | | // the temp file should have the same permissions as the original one |
551 | 0 | mode_t mode; |
552 | |
|
553 | 0 | wxStructStat st; |
554 | 0 | if ( wxStat(m_strName, &st) == 0 ) |
555 | 0 | { |
556 | 0 | mode = st.st_mode; |
557 | 0 | } |
558 | 0 | else |
559 | 0 | { |
560 | | // file probably didn't exist, just give it the default mode _using_ |
561 | | // user's umask (new files creation should respect umask) |
562 | 0 | mode_t mask = umask(0777); |
563 | 0 | mode = 0666 & ~mask; |
564 | 0 | umask(mask); |
565 | 0 | } |
566 | |
|
567 | 0 | if ( chmod( (const char*) m_strTemp.fn_str(), mode) == -1 ) |
568 | 0 | { |
569 | 0 | wxLogSysError(_("Failed to set temporary file permissions")); |
570 | 0 | } |
571 | 0 | #endif // Unix |
572 | |
|
573 | 0 | return true; |
574 | 0 | } |
575 | | |
576 | | // ---------------------------------------------------------------------------- |
577 | | // destruction |
578 | | // ---------------------------------------------------------------------------- |
579 | | |
580 | | wxTempFile::~wxTempFile() |
581 | 0 | { |
582 | 0 | if ( IsOpened() ) |
583 | 0 | Discard(); |
584 | 0 | } |
585 | | |
586 | | bool wxTempFile::Commit() |
587 | 0 | { |
588 | 0 | m_file.Close(); |
589 | |
|
590 | 0 | if ( !wxRenameFile(m_strTemp, m_strName) ) { |
591 | 0 | wxLogSysError(_("can't commit changes to file '%s'"), m_strName); |
592 | 0 | return false; |
593 | 0 | } |
594 | | |
595 | 0 | return true; |
596 | 0 | } |
597 | | |
598 | | void wxTempFile::Discard() |
599 | 0 | { |
600 | 0 | m_file.Close(); |
601 | 0 | if ( wxRemove(m_strTemp) != 0 ) |
602 | 0 | { |
603 | 0 | wxLogSysError(_("can't remove temporary file '%s'"), m_strTemp); |
604 | 0 | } |
605 | 0 | } |
606 | | |
607 | | #endif // wxUSE_FILE |
608 | | |