Coverage Report

Created: 2026-03-07 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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