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

Generated by: LCOV version 1.14