Coverage Report

Created: 2025-08-28 06:57

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