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