Coverage Report

Created: 2025-06-13 06:29

/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
#if HAVE_FCNTL_H
20
#include <fcntl.h>
21
#endif
22
23
#include "cpl_error.h"
24
#include "cpl_vsi_virtual.h"
25
26
#ifdef _WIN32
27
#include <io.h>
28
#include <fcntl.h>
29
#endif
30
31
static VSIWriteFunction pWriteFunction = fwrite;
32
static FILE *pWriteStream = stdout;
33
34
/************************************************************************/
35
/*                        VSIStdoutSetRedirection()                     */
36
/************************************************************************/
37
38
/** Set an alternative write function and output file handle instead of
39
 *  fwrite() / stdout.
40
 *
41
 * @param pFct Function with same signature as fwrite()
42
 * @param stream File handle on which to output. Passed to pFct.
43
 *
44
 * @since GDAL 2.0
45
 */
46
void VSIStdoutSetRedirection(VSIWriteFunction pFct, FILE *stream)
47
0
{
48
0
    pWriteFunction = pFct;
49
0
    pWriteStream = stream;
50
0
}
51
52
//! @cond Doxygen_Suppress
53
54
/************************************************************************/
55
/* ==================================================================== */
56
/*                       VSIStdoutFilesystemHandler                     */
57
/* ==================================================================== */
58
/************************************************************************/
59
60
class VSIStdoutFilesystemHandler final : public VSIFilesystemHandler
61
{
62
    CPL_DISALLOW_COPY_ASSIGN(VSIStdoutFilesystemHandler)
63
64
  public:
65
3
    VSIStdoutFilesystemHandler() = default;
66
67
    VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
68
                           bool bSetError,
69
                           CSLConstList /* papszOptions */) override;
70
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
71
             int nFlags) override;
72
73
    bool SupportsRead(const char * /* pszPath */) override
74
0
    {
75
0
        return false;
76
0
    }
77
};
78
79
/************************************************************************/
80
/* ==================================================================== */
81
/*                        VSIStdoutHandle                               */
82
/* ==================================================================== */
83
/************************************************************************/
84
85
class VSIStdoutHandle final : public VSIVirtualHandle
86
{
87
    CPL_DISALLOW_COPY_ASSIGN(VSIStdoutHandle)
88
89
    vsi_l_offset m_nOffset = 0;
90
    bool m_bError = false;
91
92
  public:
93
0
    VSIStdoutHandle() = default;
94
    ~VSIStdoutHandle() override = default;
95
96
    int Seek(vsi_l_offset nOffset, int nWhence) override;
97
    vsi_l_offset Tell() override;
98
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
99
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
100
101
    void ClearErr() override
102
0
    {
103
0
        m_bError = false;
104
0
    }
105
106
    int Error() override
107
0
    {
108
0
        return m_bError;
109
0
    }
110
111
    int Eof() override
112
0
    {
113
0
        return FALSE;
114
0
    }
115
116
    int Flush() override;
117
    int Close() override;
118
};
119
120
/************************************************************************/
121
/*                                Seek()                                */
122
/************************************************************************/
123
124
int VSIStdoutHandle::Seek(vsi_l_offset nOffset, int nWhence)
125
126
0
{
127
0
    if (nOffset == 0 && (nWhence == SEEK_END || nWhence == SEEK_CUR))
128
0
        return 0;
129
0
    if (nWhence == SEEK_SET && nOffset == Tell())
130
0
        return 0;
131
0
    CPLError(CE_Failure, CPLE_NotSupported, "Seek() unsupported on /vsistdout");
132
0
    return -1;
133
0
}
134
135
/************************************************************************/
136
/*                                Tell()                                */
137
/************************************************************************/
138
139
vsi_l_offset VSIStdoutHandle::Tell()
140
0
{
141
0
    return m_nOffset;
142
0
}
143
144
/************************************************************************/
145
/*                               Flush()                                */
146
/************************************************************************/
147
148
int VSIStdoutHandle::Flush()
149
150
0
{
151
0
    if (pWriteStream == stdout)
152
0
        return fflush(stdout);
153
0
    else
154
0
        return 0;
155
0
}
156
157
/************************************************************************/
158
/*                                Read()                                */
159
/************************************************************************/
160
161
size_t VSIStdoutHandle::Read(void * /* pBuffer */, size_t nSize, size_t nCount)
162
0
{
163
0
    if (nSize > 0 && nCount > 0)
164
0
    {
165
0
        CPLError(CE_Failure, CPLE_NotSupported,
166
0
                 "Read() unsupported on /vsistdout");
167
0
        m_bError = true;
168
0
    }
169
0
    return 0;
170
0
}
171
172
/************************************************************************/
173
/*                               Write()                                */
174
/************************************************************************/
175
176
size_t VSIStdoutHandle::Write(const void *pBuffer, size_t nSize, size_t nCount)
177
178
0
{
179
0
    size_t nRet = pWriteFunction(pBuffer, nSize, nCount, pWriteStream);
180
0
    m_nOffset += nSize * nRet;
181
0
    return nRet;
182
0
}
183
184
/************************************************************************/
185
/*                               Close()                                */
186
/************************************************************************/
187
188
int VSIStdoutHandle::Close()
189
190
0
{
191
0
    return Flush();
192
0
}
193
194
/************************************************************************/
195
/* ==================================================================== */
196
/*                       VSIStdoutFilesystemHandler                     */
197
/* ==================================================================== */
198
/************************************************************************/
199
200
/************************************************************************/
201
/*                                Open()                                */
202
/************************************************************************/
203
204
VSIVirtualHandle *
205
VSIStdoutFilesystemHandler::Open(const char * /* pszFilename */,
206
                                 const char *pszAccess, bool /* bSetError */,
207
                                 CSLConstList /* papszOptions */)
208
19
{
209
19
    if (strchr(pszAccess, 'r') != nullptr || strchr(pszAccess, '+') != nullptr)
210
19
    {
211
19
        CPLError(CE_Failure, CPLE_NotSupported,
212
19
                 "Read or update mode not supported on /vsistdout");
213
19
        return nullptr;
214
19
    }
215
216
#ifdef _WIN32
217
    if (strchr(pszAccess, 'b') != nullptr)
218
        setmode(fileno(stdout), O_BINARY);
219
#endif
220
221
0
    return new VSIStdoutHandle;
222
19
}
223
224
/************************************************************************/
225
/*                                Stat()                                */
226
/************************************************************************/
227
228
int VSIStdoutFilesystemHandler::Stat(const char * /* pszFilename */,
229
                                     VSIStatBufL *pStatBuf, int /* nFlags */)
230
231
84
{
232
84
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
233
234
84
    return -1;
235
84
}
236
237
/************************************************************************/
238
/* ==================================================================== */
239
/*                   VSIStdoutRedirectFilesystemHandler                 */
240
/* ==================================================================== */
241
/************************************************************************/
242
243
class VSIStdoutRedirectFilesystemHandler final : public VSIFilesystemHandler
244
{
245
  public:
246
    VSIVirtualHandle *Open(const char *pszFilename, const char *pszAccess,
247
                           bool bSetError,
248
                           CSLConstList /* papszOptions */) override;
249
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
250
             int nFlags) override;
251
252
    bool SupportsRead(const char * /* pszPath */) override
253
0
    {
254
0
        return false;
255
0
    }
256
};
257
258
/************************************************************************/
259
/* ==================================================================== */
260
/*                        VSIStdoutRedirectHandle                       */
261
/* ==================================================================== */
262
/************************************************************************/
263
264
class VSIStdoutRedirectHandle final : public VSIVirtualHandle
265
{
266
    VSIVirtualHandle *m_poHandle = nullptr;
267
    bool m_bError = false;
268
269
    CPL_DISALLOW_COPY_ASSIGN(VSIStdoutRedirectHandle)
270
271
  public:
272
    explicit VSIStdoutRedirectHandle(VSIVirtualHandle *poHandle);
273
    ~VSIStdoutRedirectHandle() override;
274
275
    int Seek(vsi_l_offset nOffset, int nWhence) override;
276
    vsi_l_offset Tell() override;
277
    size_t Read(void *pBuffer, size_t nSize, size_t nMemb) override;
278
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) override;
279
280
    void ClearErr() override
281
0
    {
282
0
        m_bError = false;
283
0
    }
284
285
    int Error() override
286
0
    {
287
0
        return m_bError;
288
0
    }
289
290
    int Eof() override
291
0
    {
292
0
        return FALSE;
293
0
    }
294
295
    int Flush() override;
296
    int Close() override;
297
};
298
299
/************************************************************************/
300
/*                        VSIStdoutRedirectHandle()                    */
301
/************************************************************************/
302
303
VSIStdoutRedirectHandle::VSIStdoutRedirectHandle(VSIVirtualHandle *poHandle)
304
0
    : m_poHandle(poHandle)
305
0
{
306
0
}
307
308
/************************************************************************/
309
/*                        ~VSIStdoutRedirectHandle()                    */
310
/************************************************************************/
311
312
VSIStdoutRedirectHandle::~VSIStdoutRedirectHandle()
313
0
{
314
0
    delete m_poHandle;
315
0
}
316
317
/************************************************************************/
318
/*                                Seek()                                */
319
/************************************************************************/
320
321
int VSIStdoutRedirectHandle::Seek(vsi_l_offset /* nOffset */, int /* nWhence */)
322
0
{
323
0
    CPLError(CE_Failure, CPLE_NotSupported,
324
0
             "Seek() unsupported on /vsistdout_redirect");
325
0
    return -1;
326
0
}
327
328
/************************************************************************/
329
/*                                Tell()                                */
330
/************************************************************************/
331
332
vsi_l_offset VSIStdoutRedirectHandle::Tell()
333
0
{
334
0
    return m_poHandle->Tell();
335
0
}
336
337
/************************************************************************/
338
/*                               Flush()                                */
339
/************************************************************************/
340
341
int VSIStdoutRedirectHandle::Flush()
342
343
0
{
344
0
    return m_poHandle->Flush();
345
0
}
346
347
/************************************************************************/
348
/*                                Read()                                */
349
/************************************************************************/
350
351
size_t VSIStdoutRedirectHandle::Read(void * /* pBuffer */, size_t nSize,
352
                                     size_t nCount)
353
0
{
354
0
    if (nSize > 0 && nCount > 0)
355
0
    {
356
0
        CPLError(CE_Failure, CPLE_NotSupported,
357
0
                 "Read() unsupported on /vsistdout");
358
0
        m_bError = true;
359
0
    }
360
0
    return 0;
361
0
}
362
363
/************************************************************************/
364
/*                               Write()                                */
365
/************************************************************************/
366
367
size_t VSIStdoutRedirectHandle::Write(const void *pBuffer, size_t nSize,
368
                                      size_t nCount)
369
370
0
{
371
0
    return m_poHandle->Write(pBuffer, nSize, nCount);
372
0
}
373
374
/************************************************************************/
375
/*                               Close()                                */
376
/************************************************************************/
377
378
int VSIStdoutRedirectHandle::Close()
379
380
0
{
381
0
    return m_poHandle->Close();
382
0
}
383
384
/************************************************************************/
385
/* ==================================================================== */
386
/*                 VSIStdoutRedirectFilesystemHandler                   */
387
/* ==================================================================== */
388
/************************************************************************/
389
390
/************************************************************************/
391
/*                                Open()                                */
392
/************************************************************************/
393
394
VSIVirtualHandle *VSIStdoutRedirectFilesystemHandler::Open(
395
    const char *pszFilename, const char *pszAccess, bool /* bSetError */,
396
    CSLConstList /* papszOptions */)
397
398
0
{
399
0
    if (strchr(pszAccess, 'r') != nullptr || strchr(pszAccess, '+') != nullptr)
400
0
    {
401
0
        CPLError(CE_Failure, CPLE_NotSupported,
402
0
                 "Read or update mode not supported on /vsistdout_redirect");
403
0
        return nullptr;
404
0
    }
405
406
0
    VSIVirtualHandle *poHandle = reinterpret_cast<VSIVirtualHandle *>(
407
0
        VSIFOpenL(pszFilename + strlen("/vsistdout_redirect/"), pszAccess));
408
0
    if (poHandle == nullptr)
409
0
        return nullptr;
410
411
0
    return new VSIStdoutRedirectHandle(poHandle);
412
0
}
413
414
/************************************************************************/
415
/*                                Stat()                                */
416
/************************************************************************/
417
418
int VSIStdoutRedirectFilesystemHandler::Stat(const char * /* pszFilename */,
419
                                             VSIStatBufL *pStatBuf,
420
                                             int /* nFlags */)
421
10
{
422
10
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
423
424
10
    return -1;
425
10
}
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
}