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