LCOV - code coverage report
Current view: top level - port - cpl_vsil_stdin.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 203 247 82.2 %
Date: 2024-11-21 22:18:42 Functions: 18 23 78.3 %

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

Generated by: LCOV version 1.14