Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_vsil_stdout.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * Project:  CPL - Common Portability Library
4
 * Purpose:  Implement VSI large file api for stdout
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 **********************************************************************
8
 * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
#include "cpl_vsi.h"
15
16
#include <cstddef>
17
#include <cstdio>
18
#include <cstring>
19
#include <fcntl.h>
20
21
#include "cpl_error.h"
22
#include "cpl_vsi_virtual.h"
23
24
#ifdef _WIN32
25
#include <io.h>
26
#endif
27
28
static VSIWriteFunction pWriteFunction = fwrite;
29
static FILE *pWriteStream = stdout;
30
31
/************************************************************************/
32
/*                        VSIStdoutSetRedirection()                     */
33
/************************************************************************/
34
35
/** Set an alternative write function and output file handle instead of
36
 *  fwrite() / stdout.
37
 *
38
 * @param pFct Function with same signature as fwrite()
39
 * @param stream File handle on which to output. Passed to pFct.
40
 *
41
 */
42
void VSIStdoutSetRedirection(VSIWriteFunction pFct, FILE *stream)
43
0
{
44
0
    pWriteFunction = pFct;
45
0
    pWriteStream = stream;
46
0
}
47
48
//! @cond Doxygen_Suppress
49
50
/************************************************************************/
51
/* ==================================================================== */
52
/*                       VSIStdoutFilesystemHandler                     */
53
/* ==================================================================== */
54
/************************************************************************/
55
56
class VSIStdoutFilesystemHandler final : public VSIFilesystemHandler
57
{
58
    CPL_DISALLOW_COPY_ASSIGN(VSIStdoutFilesystemHandler)
59
60
  public:
61
3
    VSIStdoutFilesystemHandler() = default;
62
63
    VSIVirtualHandleUniquePtr Open(const char *pszFilename,
64
                                   const char *pszAccess, bool bSetError,
65
                                   CSLConstList /* papszOptions */) override;
66
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
67
             int nFlags) override;
68
69
    bool SupportsRead(const char * /* pszPath */) override
70
0
    {
71
0
        return false;
72
0
    }
73
};
74
75
/************************************************************************/
76
/* ==================================================================== */
77
/*                        VSIStdoutHandle                               */
78
/* ==================================================================== */
79
/************************************************************************/
80
81
class VSIStdoutHandle final : public VSIVirtualHandle
82
{
83
    CPL_DISALLOW_COPY_ASSIGN(VSIStdoutHandle)
84
85
    vsi_l_offset m_nOffset = 0;
86
    bool m_bError = false;
87
88
  public:
89
0
    VSIStdoutHandle() = default;
90
    ~VSIStdoutHandle() override = default;
91
92
    int Seek(vsi_l_offset nOffset, int nWhence) override;
93
    vsi_l_offset Tell() override;
94
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
95
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
96
97
    void ClearErr() override
98
0
    {
99
0
        m_bError = false;
100
0
    }
101
102
    int Error() override
103
0
    {
104
0
        return m_bError;
105
0
    }
106
107
    int Eof() override
108
0
    {
109
0
        return FALSE;
110
0
    }
111
112
    int Flush() override;
113
    int Close() override;
114
};
115
116
/************************************************************************/
117
/*                                Seek()                                */
118
/************************************************************************/
119
120
int VSIStdoutHandle::Seek(vsi_l_offset nOffset, int nWhence)
121
122
0
{
123
0
    if (nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR))
124
0
        return 0;
125
0
    if (nWhence == SEEK_SET && nOffset == Tell())
126
0
        return 0;
127
0
    CPLError(CE_Failure, CPLE_NotSupported, "Seek() unsupported on /vsistdout");
128
0
    return -1;
129
0
}
130
131
/************************************************************************/
132
/*                                Tell()                                */
133
/************************************************************************/
134
135
vsi_l_offset VSIStdoutHandle::Tell()
136
0
{
137
0
    return m_nOffset;
138
0
}
139
140
/************************************************************************/
141
/*                               Flush()                                */
142
/************************************************************************/
143
144
int VSIStdoutHandle::Flush()
145
146
0
{
147
0
    if (pWriteStream == stdout)
148
0
        return fflush(stdout);
149
0
    else
150
0
        return 0;
151
0
}
152
153
/************************************************************************/
154
/*                                Read()                                */
155
/************************************************************************/
156
157
size_t VSIStdoutHandle::Read(void * /* pBuffer */, size_t nSize, size_t nCount)
158
0
{
159
0
    if (nSize > 0 && nCount > 0)
160
0
    {
161
0
        CPLError(CE_Failure, CPLE_NotSupported,
162
0
                 "Read() unsupported on /vsistdout");
163
0
        m_bError = true;
164
0
    }
165
0
    return 0;
166
0
}
167
168
/************************************************************************/
169
/*                               Write()                                */
170
/************************************************************************/
171
172
size_t VSIStdoutHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
173
174
0
{
175
0
    size_t nRet = pWriteFunction(pBuffer, nSize, nCount, pWriteStream);
176
0
    m_nOffset += nSize * nRet;
177
0
    return nRet;
178
0
}
179
180
/************************************************************************/
181
/*                               Close()                                */
182
/************************************************************************/
183
184
int VSIStdoutHandle::Close()
185
186
0
{
187
0
    return Flush();
188
0
}
189
190
/************************************************************************/
191
/* ==================================================================== */
192
/*                       VSIStdoutFilesystemHandler                     */
193
/* ==================================================================== */
194
/************************************************************************/
195
196
/************************************************************************/
197
/*                                Open()                                */
198
/************************************************************************/
199
200
VSIVirtualHandleUniquePtr
201
VSIStdoutFilesystemHandler::Open(const char * /* pszFilename */,
202
                                 const char *pszAccess, bool /* bSetError */,
203
                                 CSLConstList /* papszOptions */)
204
16
{
205
16
    if (strchr(pszAccess, 'r') != nullptr || strchr(pszAccess, '+') != nullptr)
206
16
    {
207
16
        CPLError(CE_Failure, CPLE_NotSupported,
208
16
                 "Read or update mode not supported on /vsistdout");
209
16
        return nullptr;
210
16
    }
211
212
#ifdef _WIN32
213
    if (strchr(pszAccess, 'b') != nullptr)
214
        setmode(fileno(stdout), O_BINARY);
215
#endif
216
217
0
    return VSIVirtualHandleUniquePtr(
218
0
        std::make_unique<VSIStdoutHandle>().release());
219
16
}
220
221
/************************************************************************/
222
/*                                Stat()                                */
223
/************************************************************************/
224
225
int VSIStdoutFilesystemHandler::Stat(const char * /* pszFilename */,
226
                                     VSIStatBufL *pStatBuf, int /* nFlags */)
227
228
41
{
229
41
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
230
231
41
    return -1;
232
41
}
233
234
/************************************************************************/
235
/* ==================================================================== */
236
/*                   VSIStdoutRedirectFilesystemHandler                 */
237
/* ==================================================================== */
238
/************************************************************************/
239
240
class VSIStdoutRedirectFilesystemHandler final : public VSIFilesystemHandler
241
{
242
  public:
243
    VSIVirtualHandleUniquePtr Open(const char *pszFilename,
244
                                   const char *pszAccess, bool bSetError,
245
                                   CSLConstList /* papszOptions */) override;
246
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
247
             int nFlags) override;
248
249
    bool SupportsRead(const char * /* pszPath */) override
250
0
    {
251
0
        return false;
252
0
    }
253
};
254
255
/************************************************************************/
256
/* ==================================================================== */
257
/*                        VSIStdoutRedirectHandle                       */
258
/* ==================================================================== */
259
/************************************************************************/
260
261
class VSIStdoutRedirectHandle final : public VSIVirtualHandle
262
{
263
    VSIVirtualHandle *m_poHandle = nullptr;
264
    bool m_bError = false;
265
266
    CPL_DISALLOW_COPY_ASSIGN(VSIStdoutRedirectHandle)
267
268
  public:
269
    explicit VSIStdoutRedirectHandle(VSIVirtualHandle *poHandle);
270
    ~VSIStdoutRedirectHandle() override;
271
272
    int Seek(vsi_l_offset nOffset, int nWhence) override;
273
    vsi_l_offset Tell() override;
274
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
275
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
276
277
    void ClearErr() override
278
0
    {
279
0
        m_bError = false;
280
0
    }
281
282
    int Error() override
283
0
    {
284
0
        return m_bError;
285
0
    }
286
287
    int Eof() override
288
0
    {
289
0
        return FALSE;
290
0
    }
291
292
    int Flush() override;
293
    int Close() override;
294
};
295
296
/************************************************************************/
297
/*                        VSIStdoutRedirectHandle()                    */
298
/************************************************************************/
299
300
VSIStdoutRedirectHandle::VSIStdoutRedirectHandle(VSIVirtualHandle *poHandle)
301
0
    : m_poHandle(poHandle)
302
0
{
303
0
}
304
305
/************************************************************************/
306
/*                        ~VSIStdoutRedirectHandle()                    */
307
/************************************************************************/
308
309
VSIStdoutRedirectHandle::~VSIStdoutRedirectHandle()
310
0
{
311
0
    delete m_poHandle;
312
0
}
313
314
/************************************************************************/
315
/*                                Seek()                                */
316
/************************************************************************/
317
318
int VSIStdoutRedirectHandle::Seek(vsi_l_offset /* nOffset */, int /* nWhence */)
319
0
{
320
0
    CPLError(CE_Failure, CPLE_NotSupported,
321
0
             "Seek() unsupported on /vsistdout_redirect");
322
0
    return -1;
323
0
}
324
325
/************************************************************************/
326
/*                                Tell()                                */
327
/************************************************************************/
328
329
vsi_l_offset VSIStdoutRedirectHandle::Tell()
330
0
{
331
0
    return m_poHandle->Tell();
332
0
}
333
334
/************************************************************************/
335
/*                               Flush()                                */
336
/************************************************************************/
337
338
int VSIStdoutRedirectHandle::Flush()
339
340
0
{
341
0
    return m_poHandle->Flush();
342
0
}
343
344
/************************************************************************/
345
/*                                Read()                                */
346
/************************************************************************/
347
348
size_t VSIStdoutRedirectHandle::Read(void * /* pBuffer */, size_t nSize,
349
                                     size_t nCount)
350
0
{
351
0
    if (nSize > 0 && nCount > 0)
352
0
    {
353
0
        CPLError(CE_Failure, CPLE_NotSupported,
354
0
                 "Read() unsupported on /vsistdout");
355
0
        m_bError = true;
356
0
    }
357
0
    return 0;
358
0
}
359
360
/************************************************************************/
361
/*                               Write()                                */
362
/************************************************************************/
363
364
size_t VSIStdoutRedirectHandle::Write(const void *pBuffer, size_t nSize,
365
                                      size_t nCount)
366
367
0
{
368
0
    return m_poHandle->Write(pBuffer, nSize, nCount);
369
0
}
370
371
/************************************************************************/
372
/*                               Close()                                */
373
/************************************************************************/
374
375
int VSIStdoutRedirectHandle::Close()
376
377
0
{
378
0
    return m_poHandle->Close();
379
0
}
380
381
/************************************************************************/
382
/* ==================================================================== */
383
/*                 VSIStdoutRedirectFilesystemHandler                   */
384
/* ==================================================================== */
385
/************************************************************************/
386
387
/************************************************************************/
388
/*                                Open()                                */
389
/************************************************************************/
390
391
VSIVirtualHandleUniquePtr VSIStdoutRedirectFilesystemHandler::Open(
392
    const char *pszFilename, const char *pszAccess, bool /* bSetError */,
393
    CSLConstList /* papszOptions */)
394
395
96
{
396
96
    if (strchr(pszAccess, 'r') != nullptr || strchr(pszAccess, '+') != nullptr)
397
96
    {
398
96
        CPLError(CE_Failure, CPLE_NotSupported,
399
96
                 "Read or update mode not supported on /vsistdout_redirect");
400
96
        return nullptr;
401
96
    }
402
403
0
    auto poHandle = VSIFilesystemHandler::OpenStatic(
404
0
        pszFilename + strlen("/vsistdout_redirect/"), pszAccess);
405
0
    if (poHandle == nullptr)
406
0
        return nullptr;
407
408
0
    return VSIVirtualHandleUniquePtr(
409
0
        std::make_unique<VSIStdoutRedirectHandle>(poHandle.release())
410
0
            .release());
411
0
}
412
413
/************************************************************************/
414
/*                                Stat()                                */
415
/************************************************************************/
416
417
int VSIStdoutRedirectFilesystemHandler::Stat(const char * /* pszFilename */,
418
                                             VSIStatBufL *pStatBuf,
419
                                             int /* nFlags */)
420
12
{
421
12
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
422
423
12
    return -1;
424
12
}
425
426
//! @endcond
427
428
/************************************************************************/
429
/*                       VSIInstallStdoutHandler()                      */
430
/************************************************************************/
431
432
/*!
433
 \brief Install /vsistdout/ file system handler
434
435
 A special file handler is installed that allows writing to the standard
436
 output stream.
437
438
 The file operations available are of course limited to Write().
439
440
 A variation of this file system exists as the /vsistdout_redirect/ file
441
 system handler, where the output function can be defined with
442
 VSIStdoutSetRedirection().
443
444
 \verbatim embed:rst
445
 See :ref:`/vsistdout/ documentation <vsistdout>`
446
 \endverbatim
447
448
 */
449
450
void VSIInstallStdoutHandler()
451
452
3
{
453
3
    VSIFileManager::InstallHandler("/vsistdout/",
454
3
                                   new VSIStdoutFilesystemHandler);
455
3
    VSIFileManager::InstallHandler("/vsistdout_redirect/",
456
3
                                   new VSIStdoutRedirectFilesystemHandler);
457
3
}