Coverage Report

Created: 2025-06-13 06:29

/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
28.0k
{
58
28.0k
    if (gpabyBuffer == nullptr)
59
28.0k
    {
60
#ifdef _WIN32
61
        setmode(fileno(stdin), O_BINARY);
62
#endif
63
28.0k
        constexpr size_t MAX_INITIAL_ALLOC = 1024 * 1024;
64
28.0k
        gnBufferAlloc = std::min(gnBufferAlloc, MAX_INITIAL_ALLOC);
65
28.0k
        gpabyBuffer = static_cast<GByte *>(CPLMalloc(gnBufferAlloc));
66
28.0k
    }
67
28.0k
}
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.92k
    VSIStdinHandle() = default;
120
121
    ~VSIStdinHandle() override
122
2.92k
    {
123
2.92k
        VSIStdinHandle::Close();
124
2.92k
    }
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
24.7k
{
142
24.7k
    CPLAssert(m_nCurOff == gnRealPos);
143
144
24.7k
    const size_t nRead = fread(pUserBuffer, 1, nToRead, gStdinFile);
145
146
24.7k
    if (gnRealPos < gnBufferLimit)
147
24.7k
    {
148
24.7k
        bool bCopyInBuffer = true;
149
24.7k
        const size_t nToCopy = static_cast<size_t>(
150
24.7k
            std::min(gnBufferLimit - gnRealPos, static_cast<uint64_t>(nRead)));
151
24.7k
        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
24.7k
        if (bCopyInBuffer)
171
24.7k
        {
172
24.7k
            memcpy(gpabyBuffer + static_cast<size_t>(gnRealPos), pUserBuffer,
173
24.7k
                   nToCopy);
174
24.7k
            gnBufferLen += nToCopy;
175
24.7k
        }
176
24.7k
    }
177
178
24.7k
    m_nCurOff += nRead;
179
24.7k
    gnRealPos = m_nCurOff;
180
181
24.7k
    if (nRead < nToRead)
182
24.7k
    {
183
24.7k
        gbHasSoughtToEnd = feof(gStdinFile);
184
24.7k
        if (gbHasSoughtToEnd)
185
24.7k
            gnFileSize = gnRealPos;
186
24.7k
        gbHasErrored = ferror(gStdinFile);
187
24.7k
    }
188
189
24.7k
    return nRead;
190
24.7k
}
191
192
/************************************************************************/
193
/*                                Seek()                                */
194
/************************************************************************/
195
196
int VSIStdinHandle::Seek(vsi_l_offset nOffset, int nWhence)
197
198
6.63k
{
199
6.63k
    m_bEOF = false;
200
201
6.63k
    if (nWhence == SEEK_SET && nOffset == m_nCurOff)
202
3.56k
        return 0;
203
204
3.07k
    VSIStdinInit();
205
206
3.07k
    if (nWhence == SEEK_END)
207
3.07k
    {
208
3.07k
        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
3.07k
        if (gbHasSoughtToEnd)
216
3.07k
        {
217
3.07k
            m_nCurOff = gnFileSize;
218
3.07k
            return 0;
219
3.07k
        }
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
3.07k
{
281
3.07k
    return m_nCurOff;
282
3.07k
}
283
284
/************************************************************************/
285
/*                                Read()                                */
286
/************************************************************************/
287
288
size_t VSIStdinHandle::Read(void *pBuffer, size_t nSize, size_t nCount)
289
290
24.9k
{
291
24.9k
    VSIStdinInit();
292
293
24.9k
    const size_t nBytesToRead = nSize * nCount;
294
24.9k
    if (nBytesToRead == 0)
295
192
        return 0;
296
297
24.7k
    if (m_nCurOff < gnRealPos && gnRealPos >= gnBufferLimit &&
298
24.7k
        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
24.7k
    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
24.7k
    const size_t nRead = ReadAndCache(pBuffer, nBytesToRead);
338
24.7k
    m_bEOF = gbHasSoughtToEnd;
339
24.7k
    m_bError = gbHasErrored;
340
24.7k
    return nRead / nSize;
341
24.7k
}
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
211
{
373
211
    return m_bError;
374
211
}
375
376
/************************************************************************/
377
/*                                Eof()                                 */
378
/************************************************************************/
379
380
int VSIStdinHandle::Eof()
381
382
90
{
383
90
    return m_bEOF;
384
90
}
385
386
/************************************************************************/
387
/*                               Close()                                */
388
/************************************************************************/
389
390
int VSIStdinHandle::Close()
391
392
5.85k
{
393
5.85k
    if (!gosStdinFilename.empty() &&
394
5.85k
        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
5.85k
    return 0;
407
5.85k
}
408
409
/************************************************************************/
410
/* ==================================================================== */
411
/*                       VSIStdinFilesystemHandler                     */
412
/* ==================================================================== */
413
/************************************************************************/
414
415
/************************************************************************/
416
/*                        VSIStdinFilesystemHandler()                   */
417
/************************************************************************/
418
419
VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
420
1
{
421
1
}
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
9.47k
{
447
9.47k
    uint64_t nVal =
448
9.47k
        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
9.47k
    constexpr size_t MAX_BUFFER_LIMIT = std::numeric_limits<size_t>::max() - 1;
454
9.47k
    if (strstr(pszBufferLimit, "MB") != nullptr)
455
505
    {
456
505
        constexpr size_t ONE_MB = 1024 * 1024;
457
505
        if (nVal > MAX_BUFFER_LIMIT / ONE_MB)
458
225
        {
459
225
            nVal = MAX_BUFFER_LIMIT;
460
225
        }
461
280
        else
462
280
        {
463
280
            nVal *= ONE_MB;
464
280
        }
465
505
    }
466
8.97k
    else if (strstr(pszBufferLimit, "GB") != nullptr)
467
807
    {
468
807
        constexpr size_t ONE_GB = 1024 * 1024 * 1024;
469
807
        if (nVal > MAX_BUFFER_LIMIT / ONE_GB)
470
328
        {
471
328
            nVal = MAX_BUFFER_LIMIT;
472
328
        }
473
479
        else
474
479
        {
475
479
            nVal *= ONE_GB;
476
479
        }
477
807
    }
478
9.47k
    if (nVal > MAX_BUFFER_LIMIT)
479
243
    {
480
243
        nVal = MAX_BUFFER_LIMIT;
481
243
    }
482
9.47k
    return static_cast<size_t>(nVal);
483
9.47k
}
484
485
/************************************************************************/
486
/*                           ParseFilename()                            */
487
/************************************************************************/
488
489
static bool ParseFilename(const char *pszFilename)
490
7.97k
{
491
7.97k
    if (!(EQUAL(pszFilename, "/vsistdin/") ||
492
7.97k
          ((STARTS_WITH(pszFilename, "/vsistdin/?") ||
493
6.51k
            STARTS_WITH(pszFilename, "/vsistdin?")) &&
494
6.51k
           strchr(pszFilename, '.') == nullptr)))
495
463
    {
496
463
        return false;
497
463
    }
498
499
7.51k
    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
7.51k
    const char *pszBufferLimit =
508
7.51k
        CPLGetConfigOption("CPL_VSISTDIN_BUFFER_LIMIT", "1048576");
509
7.51k
    size_t nBufferLimit = GetBufferLimit(pszBufferLimit);
510
511
7.51k
    pszFilename += strlen("/vsistdin/");
512
7.51k
    if (*pszFilename == '?')
513
4.14k
        pszFilename++;
514
7.51k
    char **papszTokens = CSLTokenizeString2(pszFilename, "&", 0);
515
19.9k
    for (int i = 0; papszTokens[i] != nullptr; i++)
516
12.3k
    {
517
12.3k
        char *pszUnescaped =
518
12.3k
            CPLUnescapeString(papszTokens[i], nullptr, CPLES_URL);
519
12.3k
        CPLFree(papszTokens[i]);
520
12.3k
        papszTokens[i] = pszUnescaped;
521
12.3k
    }
522
523
19.9k
    for (int i = 0; papszTokens[i]; i++)
524
12.3k
    {
525
12.3k
        char *pszKey = nullptr;
526
12.3k
        const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey);
527
12.3k
        if (pszKey && pszValue)
528
4.32k
        {
529
4.32k
            if (EQUAL(pszKey, "buffer_limit"))
530
1.96k
            {
531
1.96k
                nBufferLimit = GetBufferLimit(pszValue);
532
1.96k
            }
533
2.36k
            else
534
2.36k
            {
535
2.36k
                CPLError(CE_Warning, CPLE_NotSupported,
536
2.36k
                         "Unsupported option: %s", pszKey);
537
2.36k
            }
538
4.32k
        }
539
12.3k
        CPLFree(pszKey);
540
12.3k
    }
541
542
7.51k
    CSLDestroy(papszTokens);
543
544
    // For testing purposes
545
7.51k
    const char *pszStdinFilename =
546
7.51k
        CPLGetConfigOption("CPL_VSISTDIN_FILE", "stdin");
547
7.51k
    if (EQUAL(pszStdinFilename, "stdin"))
548
7.51k
    {
549
7.51k
        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
7.51k
    }
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
7.51k
    gnBufferLimit = std::max(gnBufferLimit, nBufferLimit);
593
594
7.51k
    return true;
595
7.51k
}
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
3.10k
{
607
3.10k
    if (!ParseFilename(pszFilename))
608
178
    {
609
178
        return nullptr;
610
178
    }
611
612
2.92k
    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.92k
    return new VSIStdinHandle();
620
2.92k
}
621
622
/************************************************************************/
623
/*                                Stat()                                */
624
/************************************************************************/
625
626
int VSIStdinFilesystemHandler::Stat(const char *pszFilename,
627
                                    VSIStatBufL *pStatBuf, int nFlags)
628
629
4.87k
{
630
4.87k
    memset(pStatBuf, 0, sizeof(VSIStatBufL));
631
632
4.87k
    if (!ParseFilename(pszFilename))
633
285
    {
634
285
        return -1;
635
285
    }
636
637
4.58k
    if (nFlags & VSI_STAT_SIZE_FLAG)
638
949
    {
639
949
        if (gbHasSoughtToEnd)
640
949
            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
949
    }
651
652
4.58k
    pStatBuf->st_mode = S_IFREG;
653
4.58k
    return 0;
654
4.58k
}
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
1
{
685
1
    auto poHandler = new VSIStdinFilesystemHandler;
686
1
    VSIFileManager::InstallHandler("/vsistdin/", poHandler);
687
1
    VSIFileManager::InstallHandler("/vsistdin?", poHandler);
688
1
}