LCOV - code coverage report
Current view: top level - port - cpl_vsil_stdin.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 201 245 82.0 %
Date: 2026-02-12 06:20:29 Functions: 17 23 73.9 %

          Line data    Source code
       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         619 : static void VSIStdinInit()
      51             : {
      52         619 :     if (gpabyBuffer == nullptr)
      53             :     {
      54             : #ifdef _WIN32
      55             :         setmode(fileno(stdin), O_BINARY);
      56             : #endif
      57           5 :         constexpr size_t MAX_INITIAL_ALLOC = 1024 * 1024;
      58           5 :         gnBufferAlloc = std::min(gnBufferAlloc, MAX_INITIAL_ALLOC);
      59           5 :         gpabyBuffer = static_cast<GByte *>(CPLMalloc(gnBufferAlloc));
      60             :     }
      61         619 : }
      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           0 :     bool SupportsSequentialWrite(const char * /* pszPath */,
      84             :                                  bool /* bAllowLocalTempFile */) override
      85             :     {
      86           0 :         return false;
      87             :     }
      88             : 
      89           0 :     bool SupportsRandomWrite(const char * /* pszPath */,
      90             :                              bool /* bAllowLocalTempFile */) override
      91             :     {
      92           0 :         return false;
      93             :     }
      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          11 :     VSIStdinHandle() = default;
     114             : 
     115          22 :     ~VSIStdinHandle() override
     116          11 :     {
     117          11 :         VSIStdinHandle::Close();
     118          22 :     }
     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        8280 : size_t VSIStdinHandle::ReadAndCache(void *pUserBuffer, size_t nToRead)
     135             : {
     136        8280 :     CPLAssert(m_nCurOff == gnRealPos);
     137             : 
     138        8280 :     const size_t nRead = fread(pUserBuffer, 1, nToRead, gStdinFile);
     139             : 
     140        8280 :     if (gnRealPos < gnBufferLimit)
     141             :     {
     142        6577 :         bool bCopyInBuffer = true;
     143             :         const size_t nToCopy = static_cast<size_t>(
     144        6577 :             std::min(gnBufferLimit - gnRealPos, static_cast<uint64_t>(nRead)));
     145        6577 :         if (gnRealPos + nToCopy > gnBufferAlloc)
     146             :         {
     147          53 :             auto newAlloc = gnRealPos + nToCopy;
     148          53 :             if (newAlloc < gnBufferLimit - newAlloc / 3)
     149          49 :                 newAlloc += newAlloc / 3;
     150             :             else
     151           4 :                 newAlloc = gnBufferLimit;
     152          53 :             GByte *newBuffer = static_cast<GByte *>(VSI_REALLOC_VERBOSE(
     153             :                 gpabyBuffer, static_cast<size_t>(newAlloc)));
     154          53 :             if (newBuffer == nullptr)
     155             :             {
     156           0 :                 bCopyInBuffer = false;
     157             :             }
     158             :             else
     159             :             {
     160          53 :                 gpabyBuffer = newBuffer;
     161          53 :                 gnBufferAlloc = static_cast<size_t>(newAlloc);
     162             :             }
     163             :         }
     164        6577 :         if (bCopyInBuffer)
     165             :         {
     166        6577 :             memcpy(gpabyBuffer + static_cast<size_t>(gnRealPos), pUserBuffer,
     167             :                    nToCopy);
     168        6577 :             gnBufferLen += nToCopy;
     169             :         }
     170             :     }
     171             : 
     172        8280 :     m_nCurOff += nRead;
     173        8280 :     gnRealPos = m_nCurOff;
     174             : 
     175        8280 :     if (nRead < nToRead)
     176             :     {
     177          10 :         gbHasSoughtToEnd = feof(gStdinFile);
     178          10 :         if (gbHasSoughtToEnd)
     179          10 :             gnFileSize = gnRealPos;
     180          10 :         gbHasErrored = ferror(gStdinFile);
     181             :     }
     182             : 
     183        8280 :     return nRead;
     184             : }
     185             : 
     186             : /************************************************************************/
     187             : /*                                Seek()                                */
     188             : /************************************************************************/
     189             : 
     190          22 : int VSIStdinHandle::Seek(vsi_l_offset nOffset, int nWhence)
     191             : 
     192             : {
     193          22 :     m_bEOF = false;
     194             : 
     195          22 :     if (nWhence == SEEK_SET && nOffset == m_nCurOff)
     196           4 :         return 0;
     197             : 
     198          18 :     VSIStdinInit();
     199             : 
     200          18 :     if (nWhence == SEEK_END)
     201             :     {
     202           6 :         if (nOffset != 0)
     203             :         {
     204           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     205             :                      "Seek(xx != 0, SEEK_END) unsupported on /vsistdin");
     206           0 :             return -1;
     207             :         }
     208             : 
     209           6 :         if (gbHasSoughtToEnd)
     210             :         {
     211           0 :             m_nCurOff = gnFileSize;
     212           0 :             return 0;
     213             :         }
     214             : 
     215           6 :         nOffset = static_cast<vsi_l_offset>(-1);
     216             :     }
     217          12 :     else if (nWhence == SEEK_CUR)
     218             :     {
     219           0 :         nOffset += m_nCurOff;
     220             :     }
     221             : 
     222          18 :     if (nWhence != SEEK_END && gnRealPos >= gnBufferLimit &&
     223           3 :         nOffset >= gnBufferLimit)
     224             :     {
     225           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     226             :                  "Backward Seek() unsupported on /vsistdin beyond "
     227             :                  "maximum buffer limit (" CPL_FRMT_GUIB " bytes).\n"
     228             :                  "This limit can be extended by setting the "
     229             :                  "CPL_VSISTDIN_BUFFER_LIMIT "
     230             :                  "configuration option to a number of bytes, or by using the "
     231             :                  "'/vsistdin?buffer_limit=number_of_bytes' filename.\n"
     232             :                  "A limit of -1 means unlimited.",
     233             :                  static_cast<GUIntBig>(gnBufferLimit));
     234           0 :         return -1;
     235             :     }
     236             : 
     237          18 :     if (nOffset < gnBufferLen)
     238             :     {
     239          10 :         m_nCurOff = nOffset;
     240          10 :         return 0;
     241             :     }
     242             : 
     243           8 :     if (nOffset == m_nCurOff)
     244           0 :         return 0;
     245             : 
     246           8 :     CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB,
     247           8 :              static_cast<GUIntBig>(m_nCurOff), nOffset);
     248             : 
     249           8 :     char abyTemp[8192] = {};
     250           8 :     m_nCurOff = gnRealPos;
     251             :     while (true)
     252             :     {
     253             :         const size_t nToRead = static_cast<size_t>(
     254       15374 :             std::min(static_cast<uint64_t>(sizeof(abyTemp)),
     255        7687 :                      static_cast<uint64_t>(nOffset - m_nCurOff)));
     256        7687 :         const size_t nRead = ReadAndCache(abyTemp, nToRead);
     257             : 
     258        7687 :         if (nRead < nToRead)
     259             :         {
     260           6 :             return nWhence == SEEK_END ? 0 : -1;
     261             :         }
     262        7681 :         if (nToRead < sizeof(abyTemp))
     263           2 :             break;
     264        7679 :     }
     265             : 
     266           2 :     return 0;
     267             : }
     268             : 
     269             : /************************************************************************/
     270             : /*                                Tell()                                */
     271             : /************************************************************************/
     272             : 
     273          23 : vsi_l_offset VSIStdinHandle::Tell()
     274             : {
     275          23 :     return m_nCurOff;
     276             : }
     277             : 
     278             : /************************************************************************/
     279             : /*                                Read()                                */
     280             : /************************************************************************/
     281             : 
     282         601 : size_t VSIStdinHandle::Read(void *pBuffer, size_t nBytesToRead)
     283             : 
     284             : {
     285         601 :     VSIStdinInit();
     286             : 
     287         601 :     if (nBytesToRead == 0)
     288           0 :         return 0;
     289             : 
     290         601 :     if (m_nCurOff < gnRealPos && gnRealPos >= gnBufferLimit &&
     291           5 :         m_nCurOff + nBytesToRead > gnBufferLimit)
     292             :     {
     293           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     294             :                  "Backward Seek() unsupported on /vsistdin beyond "
     295             :                  "maximum buffer limit (" CPL_FRMT_GUIB " bytes).\n"
     296             :                  "This limit can be extended by setting the "
     297             :                  "CPL_VSISTDIN_BUFFER_LIMIT "
     298             :                  "configuration option to a number of bytes, or by using the "
     299             :                  "'/vsistdin?buffer_limit=number_of_bytes' filename.\n"
     300             :                  "A limit of -1 means unlimited.",
     301             :                  static_cast<GUIntBig>(gnBufferLimit));
     302           1 :         return 0;
     303             :     }
     304             : 
     305         600 :     if (m_nCurOff < gnBufferLen)
     306             :     {
     307          11 :         const size_t nAlreadyCached =
     308          11 :             static_cast<size_t>(gnBufferLen - m_nCurOff);
     309          11 :         if (nBytesToRead <= nAlreadyCached)
     310             :         {
     311           7 :             memcpy(pBuffer, gpabyBuffer + static_cast<size_t>(m_nCurOff),
     312             :                    nBytesToRead);
     313           7 :             m_nCurOff += nBytesToRead;
     314           7 :             return nBytesToRead;
     315             :         }
     316             : 
     317           4 :         memcpy(pBuffer, gpabyBuffer + static_cast<size_t>(m_nCurOff),
     318             :                nAlreadyCached);
     319           4 :         m_nCurOff += nAlreadyCached;
     320             : 
     321             :         const size_t nRead =
     322           4 :             ReadAndCache(static_cast<GByte *>(pBuffer) + nAlreadyCached,
     323             :                          nBytesToRead - nAlreadyCached);
     324           4 :         m_bEOF = gbHasSoughtToEnd;
     325           4 :         m_bError = gbHasErrored;
     326             : 
     327           4 :         return nRead + nAlreadyCached;
     328             :     }
     329             : 
     330         589 :     const size_t nRead = ReadAndCache(pBuffer, nBytesToRead);
     331         589 :     m_bEOF = gbHasSoughtToEnd;
     332         589 :     m_bError = gbHasErrored;
     333         589 :     return nRead;
     334             : }
     335             : 
     336             : /************************************************************************/
     337             : /*                               Write()                                */
     338             : /************************************************************************/
     339             : 
     340           0 : size_t VSIStdinHandle::Write(const void * /* pBuffer */, size_t /* nBytes */)
     341             : {
     342           0 :     CPLError(CE_Failure, CPLE_NotSupported, "Write() unsupported on /vsistdin");
     343           0 :     return 0;
     344             : }
     345             : 
     346             : /************************************************************************/
     347             : /*                              ClearErr()                              */
     348             : /************************************************************************/
     349             : 
     350           0 : void VSIStdinHandle::ClearErr()
     351             : 
     352             : {
     353           0 :     clearerr(gStdinFile);
     354           0 :     m_bEOF = false;
     355           0 :     m_bError = false;
     356           0 : }
     357             : 
     358             : /************************************************************************/
     359             : /*                               Error()                                */
     360             : /************************************************************************/
     361             : 
     362           0 : int VSIStdinHandle::Error()
     363             : 
     364             : {
     365           0 :     return m_bError;
     366             : }
     367             : 
     368             : /************************************************************************/
     369             : /*                                Eof()                                 */
     370             : /************************************************************************/
     371             : 
     372           2 : int VSIStdinHandle::Eof()
     373             : 
     374             : {
     375           2 :     return m_bEOF;
     376             : }
     377             : 
     378             : /************************************************************************/
     379             : /*                               Close()                                */
     380             : /************************************************************************/
     381             : 
     382          22 : int VSIStdinHandle::Close()
     383             : 
     384             : {
     385          31 :     if (!gosStdinFilename.empty() &&
     386           9 :         CPLTestBool(CPLGetConfigOption("CPL_VSISTDIN_FILE_CLOSE", "NO")))
     387             :     {
     388           7 :         if (gStdinFile != stdin)
     389           7 :             fclose(gStdinFile);
     390           7 :         gStdinFile = stdin;
     391           7 :         gosStdinFilename.clear();
     392           7 :         gnRealPos = ftell(stdin);
     393           7 :         gnBufferLen = 0;
     394           7 :         gbHasSoughtToEnd = false;
     395           7 :         gbHasErrored = false;
     396           7 :         gnFileSize = 0;
     397             :     }
     398          22 :     return 0;
     399             : }
     400             : 
     401             : /************************************************************************/
     402             : /* ==================================================================== */
     403             : /*                       VSIStdinFilesystemHandler                     */
     404             : /* ==================================================================== */
     405             : /************************************************************************/
     406             : 
     407             : /************************************************************************/
     408             : /*                     VSIStdinFilesystemHandler()                      */
     409             : /************************************************************************/
     410             : 
     411        1789 : VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
     412             : {
     413        1789 : }
     414             : 
     415             : /************************************************************************/
     416             : /*                     ~VSIStdinFilesystemHandler()                     */
     417             : /************************************************************************/
     418             : 
     419        1126 : VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler()
     420             : {
     421        1126 :     if (gStdinFile != stdin)
     422           0 :         fclose(gStdinFile);
     423        1126 :     gStdinFile = stdin;
     424        1126 :     CPLFree(gpabyBuffer);
     425        1126 :     gpabyBuffer = nullptr;
     426        1126 :     gnBufferLimit = 0;
     427        1126 :     gnBufferAlloc = 0;
     428        1126 :     gnBufferLen = 0;
     429        1126 :     gnRealPos = 0;
     430        1126 :     gosStdinFilename.clear();
     431        1126 : }
     432             : 
     433             : /************************************************************************/
     434             : /*                           GetBufferLimit()                           */
     435             : /************************************************************************/
     436             : 
     437          22 : static size_t GetBufferLimit(const char *pszBufferLimit)
     438             : {
     439             :     uint64_t nVal =
     440          22 :         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          22 :     constexpr size_t MAX_BUFFER_LIMIT = std::numeric_limits<size_t>::max() - 1;
     446          22 :     if (strstr(pszBufferLimit, "MB") != nullptr)
     447             :     {
     448           1 :         constexpr size_t ONE_MB = 1024 * 1024;
     449           1 :         if (nVal > MAX_BUFFER_LIMIT / ONE_MB)
     450             :         {
     451           0 :             nVal = MAX_BUFFER_LIMIT;
     452             :         }
     453             :         else
     454             :         {
     455           1 :             nVal *= ONE_MB;
     456             :         }
     457             :     }
     458          21 :     else if (strstr(pszBufferLimit, "GB") != nullptr)
     459             :     {
     460           1 :         constexpr size_t ONE_GB = 1024 * 1024 * 1024;
     461           1 :         if (nVal > MAX_BUFFER_LIMIT / ONE_GB)
     462             :         {
     463           0 :             nVal = MAX_BUFFER_LIMIT;
     464             :         }
     465             :         else
     466             :         {
     467           1 :             nVal *= ONE_GB;
     468             :         }
     469             :     }
     470          22 :     if (nVal > MAX_BUFFER_LIMIT)
     471             :     {
     472           5 :         nVal = MAX_BUFFER_LIMIT;
     473             :     }
     474          22 :     return static_cast<size_t>(nVal);
     475             : }
     476             : 
     477             : /************************************************************************/
     478             : /*                           ParseFilename()                            */
     479             : /************************************************************************/
     480             : 
     481         143 : static bool ParseFilename(const char *pszFilename)
     482             : {
     483         143 :     if (!(EQUAL(pszFilename, "/vsistdin/") ||
     484         137 :           ((STARTS_WITH(pszFilename, "/vsistdin/?") ||
     485         137 :             STARTS_WITH(pszFilename, "/vsistdin?")) &&
     486           8 :            strchr(pszFilename, '.') == nullptr)))
     487             :     {
     488         129 :         return false;
     489             :     }
     490             : 
     491          14 :     if (!CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")))
     492             :     {
     493           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     494             :                  "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
     495             :                  "enable it");
     496           0 :         return false;
     497             :     }
     498             : 
     499             :     const char *pszBufferLimit =
     500          14 :         CPLGetConfigOption("CPL_VSISTDIN_BUFFER_LIMIT", "1048576");
     501          14 :     size_t nBufferLimit = GetBufferLimit(pszBufferLimit);
     502             : 
     503          14 :     pszFilename += strlen("/vsistdin/");
     504          14 :     if (*pszFilename == '?')
     505           0 :         pszFilename++;
     506          14 :     char **papszTokens = CSLTokenizeString2(pszFilename, "&", 0);
     507          22 :     for (int i = 0; papszTokens[i] != nullptr; i++)
     508             :     {
     509             :         char *pszUnescaped =
     510           8 :             CPLUnescapeString(papszTokens[i], nullptr, CPLES_URL);
     511           8 :         CPLFree(papszTokens[i]);
     512           8 :         papszTokens[i] = pszUnescaped;
     513             :     }
     514             : 
     515          22 :     for (int i = 0; papszTokens[i]; i++)
     516             :     {
     517           8 :         char *pszKey = nullptr;
     518           8 :         const char *pszValue = CPLParseNameValue(papszTokens[i], &pszKey);
     519           8 :         if (pszKey && pszValue)
     520             :         {
     521           8 :             if (EQUAL(pszKey, "buffer_limit"))
     522             :             {
     523           8 :                 nBufferLimit = GetBufferLimit(pszValue);
     524             :             }
     525             :             else
     526             :             {
     527           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
     528             :                          "Unsupported option: %s", pszKey);
     529             :             }
     530             :         }
     531           8 :         CPLFree(pszKey);
     532             :     }
     533             : 
     534          14 :     CSLDestroy(papszTokens);
     535             : 
     536             :     // For testing purposes
     537             :     const char *pszStdinFilename =
     538          14 :         CPLGetConfigOption("CPL_VSISTDIN_FILE", "stdin");
     539          14 :     if (EQUAL(pszStdinFilename, "stdin"))
     540             :     {
     541           4 :         if (!gosStdinFilename.empty())
     542             :         {
     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             :         }
     552             :     }
     553             :     else
     554             :     {
     555          10 :         bool bReset = false;
     556          10 :         if (gosStdinFilename != pszStdinFilename)
     557             :         {
     558           8 :             if (gStdinFile != stdin)
     559           0 :                 fclose(gStdinFile);
     560           8 :             gStdinFile = fopen(pszStdinFilename, "rb");
     561           8 :             if (gStdinFile == nullptr)
     562             :             {
     563           0 :                 gStdinFile = stdin;
     564           0 :                 return false;
     565             :             }
     566           8 :             gosStdinFilename = pszStdinFilename;
     567           8 :             bReset = true;
     568             :         }
     569             :         else
     570             :         {
     571           2 :             bReset = CPLTestBool(
     572             :                 CPLGetConfigOption("CPL_VSISTDIN_RESET_POSITION", "NO"));
     573             :         }
     574          10 :         if (bReset)
     575             :         {
     576          10 :             gnBufferLimit = 0;
     577          10 :             gnBufferLen = 0;
     578          10 :             gnRealPos = 0;
     579          10 :             gbHasSoughtToEnd = false;
     580          10 :             gnFileSize = 0;
     581             :         }
     582             :     }
     583             : 
     584          14 :     gnBufferLimit = std::max(gnBufferLimit, nBufferLimit);
     585             : 
     586          14 :     return true;
     587             : }
     588             : 
     589             : /************************************************************************/
     590             : /*                                Open()                                */
     591             : /************************************************************************/
     592             : 
     593             : VSIVirtualHandleUniquePtr
     594          43 : VSIStdinFilesystemHandler::Open(const char *pszFilename, const char *pszAccess,
     595             :                                 bool /* bSetError */,
     596             :                                 CSLConstList /* papszOptions */)
     597             : 
     598             : {
     599          43 :     if (!ParseFilename(pszFilename))
     600             :     {
     601          31 :         return nullptr;
     602             :     }
     603             : 
     604          12 :     if (strchr(pszAccess, 'w') != nullptr || strchr(pszAccess, '+') != nullptr)
     605             :     {
     606           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     607             :                  "Write or update mode not supported on /vsistdin");
     608           1 :         return nullptr;
     609             :     }
     610             : 
     611             :     return VSIVirtualHandleUniquePtr(
     612          11 :         std::make_unique<VSIStdinHandle>().release());
     613             : }
     614             : 
     615             : /************************************************************************/
     616             : /*                                Stat()                                */
     617             : /************************************************************************/
     618             : 
     619         100 : int VSIStdinFilesystemHandler::Stat(const char *pszFilename,
     620             :                                     VSIStatBufL *pStatBuf, int nFlags)
     621             : 
     622             : {
     623         100 :     memset(pStatBuf, 0, sizeof(VSIStatBufL));
     624             : 
     625         100 :     if (!ParseFilename(pszFilename))
     626             :     {
     627          98 :         return -1;
     628             :     }
     629             : 
     630           2 :     if (nFlags & VSI_STAT_SIZE_FLAG)
     631             :     {
     632           2 :         if (gbHasSoughtToEnd)
     633           0 :             pStatBuf->st_size = gnFileSize;
     634             :         else
     635             :         {
     636           2 :             auto handle = Open(pszFilename, "rb", false, nullptr);
     637           2 :             if (handle == nullptr)
     638           0 :                 return -1;
     639           2 :             handle->Seek(0, SEEK_END);
     640           2 :             pStatBuf->st_size = handle->Tell();
     641             :         }
     642             :     }
     643             : 
     644           2 :     pStatBuf->st_mode = S_IFREG;
     645           2 :     return 0;
     646             : }
     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        1789 : void VSIInstallStdinHandler()
     674             : 
     675             : {
     676        1789 :     auto poHandler = std::make_shared<VSIStdinFilesystemHandler>();
     677        1789 :     VSIFileManager::InstallHandler("/vsistdin/", poHandler);
     678        1789 :     VSIFileManager::InstallHandler("/vsistdin?", poHandler);
     679        1789 : }

Generated by: LCOV version 1.14