Coverage Report

Created: 2025-08-28 06:57

/src/gdal/port/cpl_vsil_stdin.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 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
37.7k
{
52
37.7k
    if (gpabyBuffer == nullptr)
53
37.7k
    {
54
#ifdef _WIN32
55
        setmode(fileno(stdin), O_BINARY);
56
#endif
57
37.7k
        constexpr size_t MAX_INITIAL_ALLOC = 1024 * 1024;
58
37.7k
        gnBufferAlloc = std::min(gnBufferAlloc, MAX_INITIAL_ALLOC);
59
37.7k
        gpabyBuffer = static_cast<GByte *>(CPLMalloc(gnBufferAlloc));
60
37.7k
    }
61
37.7k
}
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.50k
    VSIStdinHandle() = default;
114
115
    ~VSIStdinHandle() override
116
3.50k
    {
117
3.50k
        VSIStdinHandle::Close();
118
3.50k
    }
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 nSize, size_t nMemb) override;
123
    size_t Write(const void *pBuffer, size_t nSize, size_t nMemb) 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
33.5k
{
136
33.5k
    CPLAssert(m_nCurOff == gnRealPos);
137
138
33.5k
    const size_t nRead = fread(pUserBuffer, 1, nToRead, gStdinFile);
139
140
33.5k
    if (gnRealPos < gnBufferLimit)
141
33.5k
    {
142
33.5k
        bool bCopyInBuffer = true;
143
33.5k
        const size_t nToCopy = static_cast<size_t>(
144
33.5k
            std::min(gnBufferLimit - gnRealPos, static_cast<uint64_t>(nRead)));
145
33.5k
        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
33.5k
        if (bCopyInBuffer)
165
33.5k
        {
166
33.5k
            memcpy(gpabyBuffer + static_cast<size_t>(gnRealPos), pUserBuffer,
167
33.5k
                   nToCopy);
168
33.5k
            gnBufferLen += nToCopy;
169
33.5k
        }
170
33.5k
    }
171
172
33.5k
    m_nCurOff += nRead;
173
33.5k
    gnRealPos = m_nCurOff;
174
175
33.5k
    if (nRead < nToRead)
176
33.5k
    {
177
33.5k
        gbHasSoughtToEnd = feof(gStdinFile);
178
33.5k
        if (gbHasSoughtToEnd)
179
33.5k
            gnFileSize = gnRealPos;
180
33.5k
        gbHasErrored = ferror(gStdinFile);
181
33.5k
    }
182
183
33.5k
    return nRead;
184
33.5k
}
185
186
/************************************************************************/
187
/*                                Seek()                                */
188
/************************************************************************/
189
190
int VSIStdinHandle::Seek(vsi_l_offset nOffset, int nWhence)
191
192
11.2k
{
193
11.2k
    m_bEOF = false;
194
195
11.2k
    if (nWhence == SEEK_SET && nOffset == m_nCurOff)
196
6.97k
        return 0;
197
198
4.22k
    VSIStdinInit();
199
200
4.22k
    if (nWhence == SEEK_END)
201
4.22k
    {
202
4.22k
        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
4.22k
        if (gbHasSoughtToEnd)
210
4.22k
        {
211
4.22k
            m_nCurOff = gnFileSize;
212
4.22k
            return 0;
213
4.22k
        }
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
4.22k
{
275
4.22k
    return m_nCurOff;
276
4.22k
}
277
278
/************************************************************************/
279
/*                                Read()                                */
280
/************************************************************************/
281
282
size_t VSIStdinHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
283
284
33.5k
{
285
33.5k
    VSIStdinInit();
286
287
33.5k
    const size_t nBytesToRead = nSize * nCount;
288
33.5k
    if (nBytesToRead == 0)
289
17
        return 0;
290
291
33.5k
    if (m_nCurOff < gnRealPos && gnRealPos >= gnBufferLimit &&
292
33.5k
        m_nCurOff + nBytesToRead > gnBufferLimit)
293
0
    {
294
0
        CPLError(CE_Failure, CPLE_NotSupported,
295
0
                 "Backward Seek() unsupported on /vsistdin beyond "
296
0
                 "maximum buffer limit (" CPL_FRMT_GUIB " bytes).\n"
297
0
                 "This limit can be extended by setting the "
298
0
                 "CPL_VSISTDIN_BUFFER_LIMIT "
299
0
                 "configuration option to a number of bytes, or by using the "
300
0
                 "'/vsistdin?buffer_limit=number_of_bytes' filename.\n"
301
0
                 "A limit of -1 means unlimited.",
302
0
                 static_cast<GUIntBig>(gnBufferLimit));
303
0
        return 0;
304
0
    }
305
306
33.5k
    if (m_nCurOff < gnBufferLen)
307
0
    {
308
0
        const size_t nAlreadyCached =
309
0
            static_cast<size_t>(gnBufferLen - m_nCurOff);
310
0
        if (nBytesToRead <= nAlreadyCached)
311
0
        {
312
0
            memcpy(pBuffer, gpabyBuffer + static_cast<size_t>(m_nCurOff),
313
0
                   nBytesToRead);
314
0
            m_nCurOff += nBytesToRead;
315
0
            return nCount;
316
0
        }
317
318
0
        memcpy(pBuffer, gpabyBuffer + static_cast<size_t>(m_nCurOff),
319
0
               nAlreadyCached);
320
0
        m_nCurOff += nAlreadyCached;
321
322
0
        const size_t nRead =
323
0
            ReadAndCache(static_cast<GByte *>(pBuffer) + nAlreadyCached,
324
0
                         nBytesToRead - nAlreadyCached);
325
0
        m_bEOF = gbHasSoughtToEnd;
326
0
        m_bError = gbHasErrored;
327
328
0
        return (nRead + nAlreadyCached) / nSize;
329
0
    }
330
331
33.5k
    const size_t nRead = ReadAndCache(pBuffer, nBytesToRead);
332
33.5k
    m_bEOF = gbHasSoughtToEnd;
333
33.5k
    m_bError = gbHasErrored;
334
33.5k
    return nRead / nSize;
335
33.5k
}
336
337
/************************************************************************/
338
/*                               Write()                                */
339
/************************************************************************/
340
341
size_t VSIStdinHandle::Write(const void * /* pBuffer */, size_t /* nSize */,
342
                             size_t /* nCount */)
343
0
{
344
0
    CPLError(CE_Failure, CPLE_NotSupported, "Write() unsupported on /vsistdin");
345
0
    return 0;
346
0
}
347
348
/************************************************************************/
349
/*                             ClearErr()                               */
350
/************************************************************************/
351
352
void VSIStdinHandle::ClearErr()
353
354
0
{
355
0
    clearerr(gStdinFile);
356
0
    m_bEOF = false;
357
0
    m_bError = false;
358
0
}
359
360
/************************************************************************/
361
/*                              Error()                                 */
362
/************************************************************************/
363
364
int VSIStdinHandle::Error()
365
366
333
{
367
333
    return m_bError;
368
333
}
369
370
/************************************************************************/
371
/*                                Eof()                                 */
372
/************************************************************************/
373
374
int VSIStdinHandle::Eof()
375
376
175
{
377
175
    return m_bEOF;
378
175
}
379
380
/************************************************************************/
381
/*                               Close()                                */
382
/************************************************************************/
383
384
int VSIStdinHandle::Close()
385
386
7.01k
{
387
7.01k
    if (!gosStdinFilename.empty() &&
388
7.01k
        CPLTestBool(CPLGetConfigOption("CPL_VSISTDIN_FILE_CLOSE", "NO")))
389
0
    {
390
0
        if (gStdinFile != stdin)
391
0
            fclose(gStdinFile);
392
0
        gStdinFile = stdin;
393
0
        gosStdinFilename.clear();
394
0
        gnRealPos = ftell(stdin);
395
0
        gnBufferLen = 0;
396
0
        gbHasSoughtToEnd = false;
397
0
        gbHasErrored = false;
398
0
        gnFileSize = 0;
399
0
    }
400
7.01k
    return 0;
401
7.01k
}
402
403
/************************************************************************/
404
/* ==================================================================== */
405
/*                       VSIStdinFilesystemHandler                     */
406
/* ==================================================================== */
407
/************************************************************************/
408
409
/************************************************************************/
410
/*                        VSIStdinFilesystemHandler()                   */
411
/************************************************************************/
412
413
VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
414
3
{
415
3
}
416
417
/************************************************************************/
418
/*                       ~VSIStdinFilesystemHandler()                   */
419
/************************************************************************/
420
421
VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler()
422
0
{
423
0
    if (gStdinFile != stdin)
424
0
        fclose(gStdinFile);
425
0
    gStdinFile = stdin;
426
0
    CPLFree(gpabyBuffer);
427
0
    gpabyBuffer = nullptr;
428
0
    gnBufferLimit = 0;
429
0
    gnBufferAlloc = 0;
430
0
    gnBufferLen = 0;
431
0
    gnRealPos = 0;
432
0
    gosStdinFilename.clear();
433
0
}
434
435
/************************************************************************/
436
/*                           GetBufferLimit()                           */
437
/************************************************************************/
438
439
static size_t GetBufferLimit(const char *pszBufferLimit)
440
10.8k
{
441
10.8k
    uint64_t nVal =
442
10.8k
        static_cast<uint64_t>(std::strtoull(pszBufferLimit, nullptr, 10));
443
444
    // -1 because on 64-bit builds with size_t==uint64_t, a static analyzer
445
    // could complain that the ending nVal > MAX_BUFFER_LIMIT test is always
446
    // false
447
10.8k
    constexpr size_t MAX_BUFFER_LIMIT = std::numeric_limits<size_t>::max() - 1;
448
10.8k
    if (strstr(pszBufferLimit, "MB") != nullptr)
449
565
    {
450
565
        constexpr size_t ONE_MB = 1024 * 1024;
451
565
        if (nVal > MAX_BUFFER_LIMIT / ONE_MB)
452
190
        {
453
190
            nVal = MAX_BUFFER_LIMIT;
454
190
        }
455
375
        else
456
375
        {
457
375
            nVal *= ONE_MB;
458
375
        }
459
565
    }
460
10.2k
    else if (strstr(pszBufferLimit, "GB") != nullptr)
461
386
    {
462
386
        constexpr size_t ONE_GB = 1024 * 1024 * 1024;
463
386
        if (nVal > MAX_BUFFER_LIMIT / ONE_GB)
464
50
        {
465
50
            nVal = MAX_BUFFER_LIMIT;
466
50
        }
467
336
        else
468
336
        {
469
336
            nVal *= ONE_GB;
470
336
        }
471
386
    }
472
10.8k
    if (nVal > MAX_BUFFER_LIMIT)
473
230
    {
474
230
        nVal = MAX_BUFFER_LIMIT;
475
230
    }
476
10.8k
    return static_cast<size_t>(nVal);
477
10.8k
}
478
479
/************************************************************************/
480
/*                           ParseFilename()                            */
481
/************************************************************************/
482
483
static bool ParseFilename(const char *pszFilename)
484
10.0k
{
485
10.0k
    if (!(EQUAL(pszFilename, "/vsistdin/") ||
486
10.0k
          ((STARTS_WITH(pszFilename, "/vsistdin/?") ||
487
6.90k
            STARTS_WITH(pszFilename, "/vsistdin?")) &&
488
6.90k
           strchr(pszFilename, '.') == nullptr)))
489
410
    {
490
410
        return false;
491
410
    }
492
493
9.62k
    if (!CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")))
494
0
    {
495
0
        CPLError(CE_Failure, CPLE_NotSupported,
496
0
                 "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
497
0
                 "enable it");
498
0
        return false;
499
0
    }
500
501
9.62k
    const char *pszBufferLimit =
502
9.62k
        CPLGetConfigOption("CPL_VSISTDIN_BUFFER_LIMIT", "1048576");
503
9.62k
    size_t nBufferLimit = GetBufferLimit(pszBufferLimit);
504
505
9.62k
    pszFilename += strlen("/vsistdin/");
506
9.62k
    if (*pszFilename == '?')
507
4.70k
        pszFilename++;
508
9.62k
    char **papszTokens = CSLTokenizeString2(pszFilename, "&", 0);
509
19.3k
    for (int i = 0; papszTokens[i] != nullptr; i++)
510
9.74k
    {
511
9.74k
        char *pszUnescaped =
512
9.74k
            CPLUnescapeString(papszTokens[i], nullptr, CPLES_URL);
513
9.74k
        CPLFree(papszTokens[i]);
514
9.74k
        papszTokens[i] = pszUnescaped;
515
9.74k
    }
516
517
19.3k
    for (int i = 0; papszTokens[i]; i++)
518
9.74k
    {
519
9.74k
        char *pszKey = nullptr;
520
9.74k
        const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey);
521
9.74k
        if (pszKey && pszValue)
522
2.93k
        {
523
2.93k
            if (EQUAL(pszKey, "buffer_limit"))
524
1.22k
            {
525
1.22k
                nBufferLimit = GetBufferLimit(pszValue);
526
1.22k
            }
527
1.71k
            else
528
1.71k
            {
529
1.71k
                CPLError(CE_Warning, CPLE_NotSupported,
530
1.71k
                         "Unsupported option: %s", pszKey);
531
1.71k
            }
532
2.93k
        }
533
9.74k
        CPLFree(pszKey);
534
9.74k
    }
535
536
9.62k
    CSLDestroy(papszTokens);
537
538
    // For testing purposes
539
9.62k
    const char *pszStdinFilename =
540
9.62k
        CPLGetConfigOption("CPL_VSISTDIN_FILE", "stdin");
541
9.62k
    if (EQUAL(pszStdinFilename, "stdin"))
542
9.62k
    {
543
9.62k
        if (!gosStdinFilename.empty())
544
0
        {
545
0
            if (gStdinFile != stdin)
546
0
                fclose(gStdinFile);
547
0
            gStdinFile = stdin;
548
0
            gosStdinFilename.clear();
549
0
            gnRealPos = ftell(stdin);
550
0
            gnBufferLen = 0;
551
0
            gbHasSoughtToEnd = false;
552
0
            gnFileSize = 0;
553
0
        }
554
9.62k
    }
555
0
    else
556
0
    {
557
0
        bool bReset = false;
558
0
        if (gosStdinFilename != pszStdinFilename)
559
0
        {
560
0
            if (gStdinFile != stdin)
561
0
                fclose(gStdinFile);
562
0
            gStdinFile = fopen(pszStdinFilename, "rb");
563
0
            if (gStdinFile == nullptr)
564
0
            {
565
0
                gStdinFile = stdin;
566
0
                return false;
567
0
            }
568
0
            gosStdinFilename = pszStdinFilename;
569
0
            bReset = true;
570
0
        }
571
0
        else
572
0
        {
573
0
            bReset = CPLTestBool(
574
0
                CPLGetConfigOption("CPL_VSISTDIN_RESET_POSITION", "NO"));
575
0
        }
576
0
        if (bReset)
577
0
        {
578
0
            gnBufferLimit = 0;
579
0
            gnBufferLen = 0;
580
0
            gnRealPos = 0;
581
0
            gbHasSoughtToEnd = false;
582
0
            gnFileSize = 0;
583
0
        }
584
0
    }
585
586
9.62k
    gnBufferLimit = std::max(gnBufferLimit, nBufferLimit);
587
588
9.62k
    return true;
589
9.62k
}
590
591
/************************************************************************/
592
/*                                Open()                                */
593
/************************************************************************/
594
595
VSIVirtualHandleUniquePtr
596
VSIStdinFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
597
                                bool /* bSetError */,
598
                                CSLConstList /* papszOptions */)
599
600
3.62k
{
601
3.62k
    if (!ParseFilename(pszFilename))
602
120
    {
603
120
        return nullptr;
604
120
    }
605
606
3.50k
    if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
607
0
    {
608
0
        CPLError(CE_Failure, CPLE_NotSupported,
609
0
                 "Write or update mode not supported on /vsistdin");
610
0
        return nullptr;
611
0
    }
612
613
3.50k
    return VSIVirtualHandleUniquePtr(
614
3.50k
        std::make_unique<VSIStdinHandle>().release());
615
3.50k
}
616
617
/************************************************************************/
618
/*                                Stat()                                */
619
/************************************************************************/
620
621
int VSIStdinFilesystemHandler::Stat(const char *pszFilename,
622
                                    VSIStatBufL *pStatBuf, int nFlags)
623
624
6.40k
{
625
6.40k
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
626
627
6.40k
    if (!ParseFilename(pszFilename))
628
290
    {
629
290
        return -1;
630
290
    }
631
632
6.11k
    if (nFlags & VSI_STAT_SIZE_FLAG)
633
1.33k
    {
634
1.33k
        if (gbHasSoughtToEnd)
635
1.33k
            pStatBuf->st_size = gnFileSize;
636
0
        else
637
0
        {
638
0
            auto handle = Open(pszFilename, "rb", false, nullptr);
639
0
            if (handle == nullptr)
640
0
                return -1;
641
0
            handle->Seek(0, SEEK_END);
642
0
            pStatBuf->st_size = handle->Tell();
643
0
        }
644
1.33k
    }
645
646
6.11k
    pStatBuf->st_mode = S_IFREG;
647
6.11k
    return 0;
648
6.11k
}
649
650
//! @endcond
651
652
/************************************************************************/
653
/*                       VSIInstallStdinHandler()                       */
654
/************************************************************************/
655
656
/*!
657
 \brief Install /vsistdin/ file system handler
658
659
 A special file handler is installed that allows reading from the standard
660
 input stream.
661
662
 The file operations available are of course limited to Read() and
663
 forward Seek() (full seek in the first MB of a file by default).
664
665
 Starting with GDAL 3.6, this limit can be configured either by setting
666
 the CPL_VSISTDIN_BUFFER_LIMIT configuration option to a number of bytes
667
 (can be -1 for unlimited), or using the "/vsistdin?buffer_limit=value"
668
 filename.
669
670
 \verbatim embed:rst
671
 See :ref:`/vsistdin/ documentation <vsistdin>`
672
 \endverbatim
673
674
 @since GDAL 1.8.0
675
 */
676
void VSIInstallStdinHandler()
677
678
3
{
679
3
    auto poHandler = new VSIStdinFilesystemHandler;
680
3
    VSIFileManager::InstallHandler("/vsistdin/", poHandler);
681
3
    VSIFileManager::InstallHandler("/vsistdin?", poHandler);
682
3
}