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