Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/port/cpl_vsil_stdin.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * Project:  CPL - Common Portability Library
4
 * Purpose:  Implement VSI large file api for stdin
5
 * Author:   Even Rouault, <even dot rouault at spatialys.com>
6
 *
7
 **********************************************************************
8
 * Copyright (c) 2010-2012, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
//! @cond Doxygen_Suppress
14
15
#include "cpl_port.h"
16
#include "cpl_vsi.h"
17
18
#include <cstddef>
19
#include <cstdio>
20
#include <cstdlib>
21
#include <cstring>
22
#include <fcntl.h>
23
24
#include <algorithm>
25
#include <limits>
26
27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_vsi_virtual.h"
30
31
#ifdef _WIN32
32
#include <io.h>
33
#endif
34
35
static std::string gosStdinFilename{};
36
static FILE *gStdinFile = stdin;
37
static GByte *gpabyBuffer = nullptr;
38
static size_t gnBufferLimit = 0;  // maximum that can be allocated
39
static size_t gnBufferAlloc = 0;  // current allocation
40
static size_t gnBufferLen = 0;    // number of valid bytes in gpabyBuffer
41
static uint64_t gnRealPos = 0;    // current offset on stdin
42
static bool gbHasSoughtToEnd = false;
43
static bool gbHasErrored = false;
44
static uint64_t gnFileSize = 0;
45
46
/************************************************************************/
47
/*                            VSIStdinInit()                            */
48
/************************************************************************/
49
50
static void VSIStdinInit()
51
24.4k
{
52
24.4k
    if (gpabyBuffer == nullptr)
53
24.4k
    {
54
#ifdef _WIN32
55
        setmode(fileno(stdin), O_BINARY);
56
#endif
57
24.4k
        constexpr size_t MAX_INITIAL_ALLOC = 1024 * 1024;
58
24.4k
        gnBufferAlloc = std::min(gnBufferAlloc, MAX_INITIAL_ALLOC);
59
24.4k
        gpabyBuffer = static_cast<GByte *>(CPLMalloc(gnBufferAlloc));
60
24.4k
    }
61
24.4k
}
62
63
/************************************************************************/
64
/* ==================================================================== */
65
/*                       VSIStdinFilesystemHandler                     */
66
/* ==================================================================== */
67
/************************************************************************/
68
69
class VSIStdinFilesystemHandler final : public VSIFilesystemHandler
70
{
71
    CPL_DISALLOW_COPY_ASSIGN(VSIStdinFilesystemHandler)
72
73
  public:
74
    VSIStdinFilesystemHandler();
75
    ~VSIStdinFilesystemHandler() override;
76
77
    VSIVirtualHandleUniquePtr Open(const char *pszFilename,
78
                                   const char *pszAccess, bool bSetError,
79
                                   CSLConstList /* papszOptions */) override;
80
    int Stat(const char *pszFilename, VSIStatBufL *pStatBuf,
81
             int nFlags) override;
82
83
    bool SupportsSequentialWrite(const char * /* pszPath */,
84
                                 bool /* bAllowLocalTempFile */) override
85
0
    {
86
0
        return false;
87
0
    }
88
89
    bool SupportsRandomWrite(const char * /* pszPath */,
90
                             bool /* bAllowLocalTempFile */) override
91
0
    {
92
0
        return false;
93
0
    }
94
};
95
96
/************************************************************************/
97
/* ==================================================================== */
98
/*                        VSIStdinHandle                               */
99
/* ==================================================================== */
100
/************************************************************************/
101
102
class VSIStdinHandle final : public VSIVirtualHandle
103
{
104
  private:
105
    CPL_DISALLOW_COPY_ASSIGN(VSIStdinHandle)
106
107
    bool m_bEOF = false;
108
    bool m_bError = false;
109
    uint64_t m_nCurOff = 0;
110
    size_t ReadAndCache(void *pBuffer, size_t nToRead);
111
112
  public:
113
3.07k
    VSIStdinHandle() = default;
114
115
    ~VSIStdinHandle() override
116
3.07k
    {
117
3.07k
        VSIStdinHandle::Close();
118
3.07k
    }
119
120
    int Seek(vsi_l_offset nOffset, int nWhence) override;
121
    vsi_l_offset Tell() override;
122
    size_t Read(void *pBuffer, size_t nBytes) override;
123
    size_t Write(const void *pBuffer, size_t nBytes) override;
124
    void ClearErr() override;
125
    int Error() override;
126
    int Eof() override;
127
    int Close() override;
128
};
129
130
/************************************************************************/
131
/*                            ReadAndCache()                            */
132
/************************************************************************/
133
134
size_t VSIStdinHandle::ReadAndCache(void *pUserBuffer, size_t nToRead)
135
21.9k
{
136
21.9k
    CPLAssert(m_nCurOff == gnRealPos);
137
138
21.9k
    const size_t nRead = fread(pUserBuffer, 1, nToRead, gStdinFile);
139
140
21.9k
    if (gnRealPos < gnBufferLimit)
141
21.9k
    {
142
21.9k
        bool bCopyInBuffer = true;
143
21.9k
        const size_t nToCopy = static_cast<size_t>(
144
21.9k
            std::min(gnBufferLimit - gnRealPos, static_cast<uint64_t>(nRead)));
145
21.9k
        if (gnRealPos + nToCopy > gnBufferAlloc)
146
0
        {
147
0
            auto newAlloc = gnRealPos + nToCopy;
148
0
            if (newAlloc < gnBufferLimit - newAlloc / 3)
149
0
                newAlloc += newAlloc / 3;
150
0
            else
151
0
                newAlloc = gnBufferLimit;
152
0
            GByte *newBuffer = static_cast<GByte *>(VSI_REALLOC_VERBOSE(
153
0
                gpabyBuffer, static_cast<size_t>(newAlloc)));
154
0
            if (newBuffer == nullptr)
155
0
            {
156
0
                bCopyInBuffer = false;
157
0
            }
158
0
            else
159
0
            {
160
0
                gpabyBuffer = newBuffer;
161
0
                gnBufferAlloc = static_cast<size_t>(newAlloc);
162
0
            }
163
0
        }
164
21.9k
        if (bCopyInBuffer)
165
21.9k
        {
166
21.9k
            memcpy(gpabyBuffer + static_cast<size_t>(gnRealPos), pUserBuffer,
167
21.9k
                   nToCopy);
168
21.9k
            gnBufferLen += nToCopy;
169
21.9k
        }
170
21.9k
    }
171
172
21.9k
    m_nCurOff += nRead;
173
21.9k
    gnRealPos = m_nCurOff;
174
175
21.9k
    if (nRead < nToRead)
176
21.9k
    {
177
21.9k
        gbHasSoughtToEnd = CPL_TO_BOOL(feof(gStdinFile));
178
21.9k
        if (gbHasSoughtToEnd)
179
21.9k
            gnFileSize = gnRealPos;
180
21.9k
        gbHasErrored = CPL_TO_BOOL(ferror(gStdinFile));
181
21.9k
    }
182
183
21.9k
    return nRead;
184
21.9k
}
185
186
/************************************************************************/
187
/*                                Seek()                                */
188
/************************************************************************/
189
190
int VSIStdinHandle::Seek(vsi_l_offset nOffset, int nWhence)
191
192
7.38k
{
193
7.38k
    m_bEOF = false;
194
195
7.38k
    if (nWhence == SEEK_SET && nOffset == m_nCurOff)
196
4.91k
        return 0;
197
198
2.47k
    VSIStdinInit();
199
200
2.47k
    if (nWhence == SEEK_END)
201
2.47k
    {
202
2.47k
        if (nOffset != 0)
203
0
        {
204
0
            CPLError(CE_Failure, CPLE_NotSupported,
205
0
                     "Seek(xx != 0, SEEK_END) unsupported on /vsistdin");
206
0
            return -1;
207
0
        }
208
209
2.47k
        if (gbHasSoughtToEnd)
210
2.47k
        {
211
2.47k
            m_nCurOff = gnFileSize;
212
2.47k
            return 0;
213
2.47k
        }
214
215
0
        nOffset = static_cast<vsi_l_offset>(-1);
216
0
    }
217
0
    else if (nWhence == SEEK_CUR)
218
0
    {
219
0
        nOffset += m_nCurOff;
220
0
    }
221
222
0
    if (nWhence != SEEK_END && gnRealPos >= gnBufferLimit &&
223
0
        nOffset >= gnBufferLimit)
224
0
    {
225
0
        CPLError(CE_Failure, CPLE_NotSupported,
226
0
                 "Backward Seek() unsupported on /vsistdin beyond "
227
0
                 "maximum buffer limit (" CPL_FRMT_GUIB " bytes).\n"
228
0
                 "This limit can be extended by setting the "
229
0
                 "CPL_VSISTDIN_BUFFER_LIMIT "
230
0
                 "configuration option to a number of bytes, or by using the "
231
0
                 "'/vsistdin?buffer_limit=number_of_bytes' filename.\n"
232
0
                 "A limit of -1 means unlimited.",
233
0
                 static_cast<GUIntBig>(gnBufferLimit));
234
0
        return -1;
235
0
    }
236
237
0
    if (nOffset < gnBufferLen)
238
0
    {
239
0
        m_nCurOff = nOffset;
240
0
        return 0;
241
0
    }
242
243
0
    if (nOffset == m_nCurOff)
244
0
        return 0;
245
246
0
    CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB,
247
0
             static_cast<GUIntBig>(m_nCurOff), nOffset);
248
249
0
    char abyTemp[8192] = {};
250
0
    m_nCurOff = gnRealPos;
251
0
    while (true)
252
0
    {
253
0
        const size_t nToRead = static_cast<size_t>(
254
0
            std::min(static_cast<uint64_t>(sizeof(abyTemp)),
255
0
                     static_cast<uint64_t>(nOffset - m_nCurOff)));
256
0
        const size_t nRead = ReadAndCache(abyTemp, nToRead);
257
258
0
        if (nRead < nToRead)
259
0
        {
260
0
            return nWhence == SEEK_END ? 0 : -1;
261
0
        }
262
0
        if (nToRead < sizeof(abyTemp))
263
0
            break;
264
0
    }
265
266
0
    return 0;
267
0
}
268
269
/************************************************************************/
270
/*                                Tell()                                */
271
/************************************************************************/
272
273
vsi_l_offset VSIStdinHandle::Tell()
274
2.47k
{
275
2.47k
    return m_nCurOff;
276
2.47k
}
277
278
/************************************************************************/
279
/*                                Read()                                */
280
/************************************************************************/
281
282
size_t VSIStdinHandle::Read(void *pBuffer, size_t nBytesToRead)
283
284
21.9k
{
285
21.9k
    VSIStdinInit();
286
287
21.9k
    if (nBytesToRead == 0)
288
0
        return 0;
289
290
21.9k
    if (m_nCurOff < gnRealPos && gnRealPos >= gnBufferLimit &&
291
0
        m_nCurOff + nBytesToRead > gnBufferLimit)
292
0
    {
293
0
        CPLError(CE_Failure, CPLE_NotSupported,
294
0
                 "Backward Seek() unsupported on /vsistdin beyond "
295
0
                 "maximum buffer limit (" CPL_FRMT_GUIB " bytes).\n"
296
0
                 "This limit can be extended by setting the "
297
0
                 "CPL_VSISTDIN_BUFFER_LIMIT "
298
0
                 "configuration option to a number of bytes, or by using the "
299
0
                 "'/vsistdin?buffer_limit=number_of_bytes' filename.\n"
300
0
                 "A limit of -1 means unlimited.",
301
0
                 static_cast<GUIntBig>(gnBufferLimit));
302
0
        return 0;
303
0
    }
304
305
21.9k
    if (m_nCurOff < gnBufferLen)
306
0
    {
307
0
        const size_t nAlreadyCached =
308
0
            static_cast<size_t>(gnBufferLen - m_nCurOff);
309
0
        if (nBytesToRead <= nAlreadyCached)
310
0
        {
311
0
            memcpy(pBuffer, gpabyBuffer + static_cast<size_t>(m_nCurOff),
312
0
                   nBytesToRead);
313
0
            m_nCurOff += nBytesToRead;
314
0
            return nBytesToRead;
315
0
        }
316
317
0
        memcpy(pBuffer, gpabyBuffer + static_cast<size_t>(m_nCurOff),
318
0
               nAlreadyCached);
319
0
        m_nCurOff += nAlreadyCached;
320
321
0
        const size_t nRead =
322
0
            ReadAndCache(static_cast<GByte *>(pBuffer) + nAlreadyCached,
323
0
                         nBytesToRead - nAlreadyCached);
324
0
        m_bEOF = gbHasSoughtToEnd;
325
0
        m_bError = gbHasErrored;
326
327
0
        return nRead + nAlreadyCached;
328
0
    }
329
330
21.9k
    const size_t nRead = ReadAndCache(pBuffer, nBytesToRead);
331
21.9k
    m_bEOF = gbHasSoughtToEnd;
332
21.9k
    m_bError = gbHasErrored;
333
21.9k
    return nRead;
334
21.9k
}
335
336
/************************************************************************/
337
/*                               Write()                                */
338
/************************************************************************/
339
340
size_t VSIStdinHandle::Write(const void * /* pBuffer */, size_t /* nBytes */)
341
0
{
342
0
    CPLError(CE_Failure, CPLE_NotSupported, "Write() unsupported on /vsistdin");
343
0
    return 0;
344
0
}
345
346
/************************************************************************/
347
/*                              ClearErr()                              */
348
/************************************************************************/
349
350
void VSIStdinHandle::ClearErr()
351
352
0
{
353
0
    clearerr(gStdinFile);
354
0
    m_bEOF = false;
355
0
    m_bError = false;
356
0
}
357
358
/************************************************************************/
359
/*                               Error()                                */
360
/************************************************************************/
361
362
int VSIStdinHandle::Error()
363
364
157
{
365
157
    return m_bError;
366
157
}
367
368
/************************************************************************/
369
/*                                Eof()                                 */
370
/************************************************************************/
371
372
int VSIStdinHandle::Eof()
373
374
180
{
375
180
    return m_bEOF;
376
180
}
377
378
/************************************************************************/
379
/*                               Close()                                */
380
/************************************************************************/
381
382
int VSIStdinHandle::Close()
383
384
6.15k
{
385
6.15k
    if (!gosStdinFilename.empty() &&
386
0
        CPLTestBool(CPLGetConfigOption("CPL_VSISTDIN_FILE_CLOSE", "NO")))
387
0
    {
388
0
        if (gStdinFile != stdin)
389
0
            fclose(gStdinFile);
390
0
        gStdinFile = stdin;
391
0
        gosStdinFilename.clear();
392
0
        gnRealPos = ftell(stdin);
393
0
        gnBufferLen = 0;
394
0
        gbHasSoughtToEnd = false;
395
0
        gbHasErrored = false;
396
0
        gnFileSize = 0;
397
0
    }
398
6.15k
    return 0;
399
6.15k
}
400
401
/************************************************************************/
402
/* ==================================================================== */
403
/*                       VSIStdinFilesystemHandler                     */
404
/* ==================================================================== */
405
/************************************************************************/
406
407
/************************************************************************/
408
/*                     VSIStdinFilesystemHandler()                      */
409
/************************************************************************/
410
411
VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
412
4
{
413
4
}
414
415
/************************************************************************/
416
/*                     ~VSIStdinFilesystemHandler()                     */
417
/************************************************************************/
418
419
VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler()
420
0
{
421
0
    if (gStdinFile != stdin)
422
0
        fclose(gStdinFile);
423
0
    gStdinFile = stdin;
424
0
    CPLFree(gpabyBuffer);
425
0
    gpabyBuffer = nullptr;
426
0
    gnBufferLimit = 0;
427
0
    gnBufferAlloc = 0;
428
0
    gnBufferLen = 0;
429
0
    gnRealPos = 0;
430
0
    gosStdinFilename.clear();
431
0
}
432
433
/************************************************************************/
434
/*                           GetBufferLimit()                           */
435
/************************************************************************/
436
437
static size_t GetBufferLimit(const char *pszBufferLimit)
438
10.5k
{
439
10.5k
    uint64_t nVal =
440
10.5k
        static_cast<uint64_t>(std::strtoull(pszBufferLimit, nullptr, 10));
441
442
    // -1 because on 64-bit builds with size_t==uint64_t, a static analyzer
443
    // could complain that the ending nVal > MAX_BUFFER_LIMIT test is always
444
    // false
445
10.5k
    constexpr size_t MAX_BUFFER_LIMIT = std::numeric_limits<size_t>::max() - 1;
446
10.5k
    if (strstr(pszBufferLimit, "MB") != nullptr)
447
358
    {
448
358
        constexpr size_t ONE_MB = 1024 * 1024;
449
358
        if (nVal > MAX_BUFFER_LIMIT / ONE_MB)
450
234
        {
451
234
            nVal = MAX_BUFFER_LIMIT;
452
234
        }
453
124
        else
454
124
        {
455
124
            nVal *= ONE_MB;
456
124
        }
457
358
    }
458
10.1k
    else if (strstr(pszBufferLimit, "GB") != nullptr)
459
395
    {
460
395
        constexpr size_t ONE_GB = 1024 * 1024 * 1024;
461
395
        if (nVal > MAX_BUFFER_LIMIT / ONE_GB)
462
3
        {
463
3
            nVal = MAX_BUFFER_LIMIT;
464
3
        }
465
392
        else
466
392
        {
467
392
            nVal *= ONE_GB;
468
392
        }
469
395
    }
470
10.5k
    if (nVal > MAX_BUFFER_LIMIT)
471
635
    {
472
635
        nVal = MAX_BUFFER_LIMIT;
473
635
    }
474
10.5k
    return static_cast<size_t>(nVal);
475
10.5k
}
476
477
/************************************************************************/
478
/*                           ParseFilename()                            */
479
/************************************************************************/
480
481
static bool ParseFilename(const char *pszFilename)
482
8.82k
{
483
8.82k
    if (!(EQUAL(pszFilename, "/vsistdin/") ||
484
6.30k
          ((STARTS_WITH(pszFilename, "/vsistdin/?") ||
485
2.97k
            STARTS_WITH(pszFilename, "/vsistdin?")) &&
486
6.21k
           strchr(pszFilename, '.') == nullptr)))
487
274
    {
488
274
        return false;
489
274
    }
490
491
8.54k
    if (!CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")))
492
0
    {
493
0
        CPLError(CE_Failure, CPLE_NotSupported,
494
0
                 "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
495
0
                 "enable it");
496
0
        return false;
497
0
    }
498
499
8.54k
    const char *pszBufferLimit =
500
8.54k
        CPLGetConfigOption("CPL_VSISTDIN_BUFFER_LIMIT", "1048576");
501
8.54k
    size_t nBufferLimit = GetBufferLimit(pszBufferLimit);
502
503
8.54k
    pszFilename += strlen("/vsistdin/");
504
8.54k
    if (*pszFilename == '?')
505
3.15k
        pszFilename++;
506
8.54k
    char **papszTokens = CSLTokenizeString2(pszFilename, "&", 0);
507
19.7k
    for (int i = 0; papszTokens[i] != nullptr; i++)
508
11.2k
    {
509
11.2k
        char *pszUnescaped =
510
11.2k
            CPLUnescapeString(papszTokens[i], nullptr, CPLES_URL);
511
11.2k
        CPLFree(papszTokens[i]);
512
11.2k
        papszTokens[i] = pszUnescaped;
513
11.2k
    }
514
515
19.7k
    for (int i = 0; papszTokens[i]; i++)
516
11.2k
    {
517
11.2k
        char *pszKey = nullptr;
518
11.2k
        const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey);
519
11.2k
        if (pszKey && pszValue)
520
3.14k
        {
521
3.14k
            if (EQUAL(pszKey, "buffer_limit"))
522
1.98k
            {
523
1.98k
                nBufferLimit = GetBufferLimit(pszValue);
524
1.98k
            }
525
1.15k
            else
526
1.15k
            {
527
1.15k
                CPLError(CE_Warning, CPLE_NotSupported,
528
1.15k
                         "Unsupported option: %s", pszKey);
529
1.15k
            }
530
3.14k
        }
531
11.2k
        CPLFree(pszKey);
532
11.2k
    }
533
534
8.54k
    CSLDestroy(papszTokens);
535
536
    // For testing purposes
537
8.54k
    const char *pszStdinFilename =
538
8.54k
        CPLGetConfigOption("CPL_VSISTDIN_FILE", "stdin");
539
8.54k
    if (EQUAL(pszStdinFilename, "stdin"))
540
8.54k
    {
541
8.54k
        if (!gosStdinFilename.empty())
542
0
        {
543
0
            if (gStdinFile != stdin)
544
0
                fclose(gStdinFile);
545
0
            gStdinFile = stdin;
546
0
            gosStdinFilename.clear();
547
0
            gnRealPos = ftell(stdin);
548
0
            gnBufferLen = 0;
549
0
            gbHasSoughtToEnd = false;
550
0
            gnFileSize = 0;
551
0
        }
552
8.54k
    }
553
0
    else
554
0
    {
555
0
        bool bReset = false;
556
0
        if (gosStdinFilename != pszStdinFilename)
557
0
        {
558
0
            if (gStdinFile != stdin)
559
0
                fclose(gStdinFile);
560
0
            gStdinFile = fopen(pszStdinFilename, "rb");
561
0
            if (gStdinFile == nullptr)
562
0
            {
563
0
                gStdinFile = stdin;
564
0
                return false;
565
0
            }
566
0
            gosStdinFilename = pszStdinFilename;
567
0
            bReset = true;
568
0
        }
569
0
        else
570
0
        {
571
0
            bReset = CPLTestBool(
572
0
                CPLGetConfigOption("CPL_VSISTDIN_RESET_POSITION", "NO"));
573
0
        }
574
0
        if (bReset)
575
0
        {
576
0
            gnBufferLimit = 0;
577
0
            gnBufferLen = 0;
578
0
            gnRealPos = 0;
579
0
            gbHasSoughtToEnd = false;
580
0
            gnFileSize = 0;
581
0
        }
582
0
    }
583
584
8.54k
    gnBufferLimit = std::max(gnBufferLimit, nBufferLimit);
585
586
8.54k
    return true;
587
8.54k
}
588
589
/************************************************************************/
590
/*                                Open()                                */
591
/************************************************************************/
592
593
VSIVirtualHandleUniquePtr
594
VSIStdinFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
595
                                bool /* bSetError */,
596
                                CSLConstList /* papszOptions */)
597
598
3.13k
{
599
3.13k
    if (!ParseFilename(pszFilename))
600
60
    {
601
60
        return nullptr;
602
60
    }
603
604
3.07k
    if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
605
0
    {
606
0
        CPLError(CE_Failure, CPLE_NotSupported,
607
0
                 "Write or update mode not supported on /vsistdin");
608
0
        return nullptr;
609
0
    }
610
611
3.07k
    return VSIVirtualHandleUniquePtr(
612
3.07k
        std::make_unique<VSIStdinHandle>().release());
613
3.07k
}
614
615
/************************************************************************/
616
/*                                Stat()                                */
617
/************************************************************************/
618
619
int VSIStdinFilesystemHandler::Stat(const char *pszFilename,
620
                                    VSIStatBufL *pStatBuf, int nFlags)
621
622
5.68k
{
623
5.68k
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
624
625
5.68k
    if (!ParseFilename(pszFilename))
626
214
    {
627
214
        return -1;
628
214
    }
629
630
5.47k
    if (nFlags & VSI_STAT_SIZE_FLAG)
631
1.52k
    {
632
1.52k
        if (gbHasSoughtToEnd)
633
1.52k
            pStatBuf->st_size = gnFileSize;
634
0
        else
635
0
        {
636
0
            auto handle = Open(pszFilename, "rb", false, nullptr);
637
0
            if (handle == nullptr)
638
0
                return -1;
639
0
            handle->Seek(0, SEEK_END);
640
0
            pStatBuf->st_size = handle->Tell();
641
0
        }
642
1.52k
    }
643
644
5.47k
    pStatBuf->st_mode = S_IFREG;
645
5.47k
    return 0;
646
5.47k
}
647
648
//! @endcond
649
650
/************************************************************************/
651
/*                       VSIInstallStdinHandler()                       */
652
/************************************************************************/
653
654
/*!
655
 \brief Install /vsistdin/ file system handler
656
657
 A special file handler is installed that allows reading from the standard
658
 input stream.
659
660
 The file operations available are of course limited to Read() and
661
 forward Seek() (full seek in the first MB of a file by default).
662
663
 Starting with GDAL 3.6, this limit can be configured either by setting
664
 the CPL_VSISTDIN_BUFFER_LIMIT configuration option to a number of bytes
665
 (can be -1 for unlimited), or using the "/vsistdin?buffer_limit=value"
666
 filename.
667
668
 \verbatim embed:rst
669
 See :ref:`/vsistdin/ documentation <vsistdin>`
670
 \endverbatim
671
672
 */
673
void VSIInstallStdinHandler()
674
675
4
{
676
4
    auto poHandler = std::make_shared<VSIStdinFilesystemHandler>();
677
4
    VSIFileManager::InstallHandler("/vsistdin/", poHandler);
678
4
    VSIFileManager::InstallHandler("/vsistdin?", poHandler);
679
4
}