Coverage Report

Created: 2026-02-14 06:52

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) override;
95
    size_t Write(const void *pBuffer, size_t nBytes) 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 nBytes)
158
0
{
159
0
    if (nBytes > 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 nBytes)
173
174
0
{
175
0
    size_t nRet = pWriteFunction(pBuffer, 1, nBytes, pWriteStream);
176
0
    m_nOffset += 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
2
{
205
2
    if (strchr(pszAccess, 'r') != nullptr || strchr(pszAccess, '+') != nullptr)
206
2
    {
207
2
        CPLError(CE_Failure, CPLE_NotSupported,
208
2
                 "Read or update mode not supported on /vsistdout");
209
2
        return nullptr;
210
2
    }
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
2
}
220
221
/************************************************************************/
222
/*                                Stat()                                */
223
/************************************************************************/
224
225
int VSIStdoutFilesystemHandler::Stat(const char * /* pszFilename */,
226
                                     VSIStatBufL *pStatBuf, int /* nFlags */)
227
228
94
{
229
94
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
230
231
94
    return -1;
232
94
}
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) override;
275
    size_t Write(const void *pBuffer, size_t nBytes) 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 nBytes)
349
0
{
350
0
    if (nBytes)
351
0
    {
352
0
        CPLError(CE_Failure, CPLE_NotSupported,
353
0
                 "Read() unsupported on /vsistdout");
354
0
        m_bError = true;
355
0
    }
356
0
    return 0;
357
0
}
358
359
/************************************************************************/
360
/*                               Write()                                */
361
/************************************************************************/
362
363
size_t VSIStdoutRedirectHandle::Write(const void *pBuffer, size_t nBytes)
364
365
0
{
366
0
    return m_poHandle->Write(pBuffer, nBytes);
367
0
}
368
369
/************************************************************************/
370
/*                               Close()                                */
371
/************************************************************************/
372
373
int VSIStdoutRedirectHandle::Close()
374
375
0
{
376
0
    return m_poHandle->Close();
377
0
}
378
379
/************************************************************************/
380
/* ==================================================================== */
381
/*                 VSIStdoutRedirectFilesystemHandler                   */
382
/* ==================================================================== */
383
/************************************************************************/
384
385
/************************************************************************/
386
/*                                Open()                                */
387
/************************************************************************/
388
389
VSIVirtualHandleUniquePtr VSIStdoutRedirectFilesystemHandler::Open(
390
    const char *pszFilename, const char *pszAccess, bool /* bSetError */,
391
    CSLConstList /* papszOptions */)
392
393
106
{
394
106
    if (strchr(pszAccess, 'r') != nullptr || strchr(pszAccess, '+') != nullptr)
395
106
    {
396
106
        CPLError(CE_Failure, CPLE_NotSupported,
397
106
                 "Read or update mode not supported on /vsistdout_redirect");
398
106
        return nullptr;
399
106
    }
400
401
0
    auto poHandle = VSIFilesystemHandler::OpenStatic(
402
0
        pszFilename + strlen("/vsistdout_redirect/"), pszAccess);
403
0
    if (poHandle == nullptr)
404
0
        return nullptr;
405
406
0
    return VSIVirtualHandleUniquePtr(
407
0
        std::make_unique<VSIStdoutRedirectHandle>(poHandle.release())
408
0
            .release());
409
0
}
410
411
/************************************************************************/
412
/*                                Stat()                                */
413
/************************************************************************/
414
415
int VSIStdoutRedirectFilesystemHandler::Stat(const char * /* pszFilename */,
416
                                             VSIStatBufL *pStatBuf,
417
                                             int /* nFlags */)
418
5
{
419
5
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
420
421
5
    return -1;
422
5
}
423
424
//! @endcond
425
426
/************************************************************************/
427
/*                      VSIInstallStdoutHandler()                       */
428
/************************************************************************/
429
430
/*!
431
 \brief Install /vsistdout/ file system handler
432
433
 A special file handler is installed that allows writing to the standard
434
 output stream.
435
436
 The file operations available are of course limited to Write().
437
438
 A variation of this file system exists as the /vsistdout_redirect/ file
439
 system handler, where the output function can be defined with
440
 VSIStdoutSetRedirection().
441
442
 \verbatim embed:rst
443
 See :ref:`/vsistdout/ documentation <vsistdout>`
444
 \endverbatim
445
446
 */
447
448
void VSIInstallStdoutHandler()
449
450
3
{
451
3
    VSIFileManager::InstallHandler(
452
3
        "/vsistdout/", std::make_shared<VSIStdoutFilesystemHandler>());
453
3
    VSIFileManager::InstallHandler(
454
3
        "/vsistdout_redirect/",
455
3
        std::make_shared<VSIStdoutRedirectFilesystemHandler>());
456
3
}