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