Coverage Report

Created: 2025-06-22 06:59

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