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 <cstring>
37 #if HAVE_FCNTL_H
38 # include <fcntl.h>
39 #endif
40 #if HAVE_SYS_STAT_H
41 #include <sys/stat.h>
42 #endif
43
44 #include <algorithm>
45
46 #include "cpl_conv.h"
47 #include "cpl_error.h"
48 #include "cpl_vsi_virtual.h"
49
50 #ifdef WIN32
51 #include <io.h>
52 #include <fcntl.h>
53 #endif
54
55 CPL_CVSID("$Id: cpl_vsil_stdin.cpp a044c83f8091becdd11e27be6e9c08d0d3478126 2021-02-24 11:38:17 +0100 Even Rouault $")
56
57 // We buffer the first 1MB of standard input to enable drivers
58 // to autodetect data. In the first MB, backward and forward seeking
59 // is allowed, after only forward seeking will work.
60 // TODO(schwehr): Make BUFFER_SIZE a static const.
61 #define BUFFER_SIZE (1024 * 1024)
62
63 static GByte* pabyBuffer = nullptr;
64 static GUInt32 nBufferLen = 0;
65 static GUIntBig nRealPos = 0;
66
67 /************************************************************************/
68 /* VSIStdinInit() */
69 /************************************************************************/
70
VSIStdinInit()71 static void VSIStdinInit()
72 {
73 if( pabyBuffer == nullptr )
74 {
75 #ifdef WIN32
76 setmode( fileno( stdin ), O_BINARY );
77 #endif
78 pabyBuffer = static_cast<GByte *>(CPLMalloc(BUFFER_SIZE));
79 }
80 }
81
82 /************************************************************************/
83 /* ==================================================================== */
84 /* VSIStdinFilesystemHandler */
85 /* ==================================================================== */
86 /************************************************************************/
87
88 class VSIStdinFilesystemHandler final : public VSIFilesystemHandler
89 {
90 CPL_DISALLOW_COPY_ASSIGN(VSIStdinFilesystemHandler)
91
92 public:
93 VSIStdinFilesystemHandler();
94 ~VSIStdinFilesystemHandler() override;
95
96 VSIVirtualHandle *Open( const char *pszFilename,
97 const char *pszAccess,
98 bool bSetError,
99 CSLConstList /* papszOptions */ ) override;
100 int Stat( const char *pszFilename, VSIStatBufL *pStatBuf,
101 int nFlags ) override;
102 };
103
104 /************************************************************************/
105 /* ==================================================================== */
106 /* VSIStdinHandle */
107 /* ==================================================================== */
108 /************************************************************************/
109
110 class VSIStdinHandle final : public VSIVirtualHandle
111 {
112 private:
113 CPL_DISALLOW_COPY_ASSIGN(VSIStdinHandle)
114
115 GUIntBig nCurOff = 0;
116 int ReadAndCache( void* pBuffer, int nToRead );
117
118 public:
119 VSIStdinHandle() = default;
120 ~VSIStdinHandle() override = default;
121
122 int Seek( vsi_l_offset nOffset, int nWhence ) override;
123 vsi_l_offset Tell() override;
124 size_t Read( void *pBuffer, size_t nSize, size_t nMemb ) override;
125 size_t Write( const void *pBuffer, size_t nSize, size_t nMemb ) override;
126 int Eof() override;
127 int Close() override;
128 };
129
130 /************************************************************************/
131 /* ReadAndCache() */
132 /************************************************************************/
133
ReadAndCache(void * pBuffer,int nToRead)134 int VSIStdinHandle::ReadAndCache( void* pBuffer, int nToRead )
135 {
136 CPLAssert(nCurOff == nRealPos);
137
138 int nRead = static_cast<int>(fread(pBuffer, 1, nToRead, stdin));
139
140 if( nRealPos < BUFFER_SIZE )
141 {
142 const int nToCopy =
143 std::min(BUFFER_SIZE - static_cast<int>(nRealPos), nRead);
144 memcpy(pabyBuffer + nRealPos, pBuffer, nToCopy);
145 nBufferLen += nToCopy;
146 }
147
148 nCurOff += nRead;
149 nRealPos = nCurOff;
150
151 return nRead;
152 }
153
154 /************************************************************************/
155 /* Seek() */
156 /************************************************************************/
157
Seek(vsi_l_offset nOffset,int nWhence)158 int VSIStdinHandle::Seek( vsi_l_offset nOffset, int nWhence )
159
160 {
161 if( nWhence == SEEK_SET && nOffset == nCurOff )
162 return 0;
163
164 VSIStdinInit();
165 if( nRealPos < BUFFER_SIZE )
166 {
167 nRealPos += fread(pabyBuffer + nRealPos, 1,
168 BUFFER_SIZE - static_cast<int>(nRealPos), stdin);
169 nBufferLen = static_cast<int>(nRealPos);
170 }
171
172 if( nWhence == SEEK_END )
173 {
174 if( nOffset != 0 )
175 {
176 CPLError(CE_Failure, CPLE_NotSupported,
177 "Seek(xx != 0, SEEK_END) unsupported on /vsistdin");
178 return -1;
179 }
180
181 if( nBufferLen < BUFFER_SIZE )
182 {
183 nCurOff = nBufferLen;
184 return 0;
185 }
186
187 CPLError(CE_Failure, CPLE_NotSupported,
188 "Seek(SEEK_END) unsupported on /vsistdin when stdin > 1 MB");
189 return -1;
190 }
191
192 if( nWhence == SEEK_CUR )
193 nOffset += nCurOff;
194
195 if( nRealPos > nBufferLen && nOffset < nRealPos )
196 {
197 CPLError(CE_Failure, CPLE_NotSupported,
198 "backward Seek() unsupported on /vsistdin above first MB");
199 return -1;
200 }
201
202 if( nOffset < nBufferLen )
203 {
204 nCurOff = nOffset;
205 return 0;
206 }
207
208 if( nOffset == nCurOff )
209 return 0;
210
211 CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB,
212 nCurOff, nOffset);
213
214 char abyTemp[8192] = {};
215 nCurOff = nRealPos;
216 while( true )
217 {
218 const vsi_l_offset nMaxToRead = 8192;
219 const int nToRead = static_cast<int>(std::min(nMaxToRead,
220 nOffset - nCurOff));
221 const int nRead = ReadAndCache(abyTemp, nToRead);
222
223 if( nRead < nToRead )
224 return -1;
225 if( nToRead < 8192 )
226 break;
227 }
228
229 return 0;
230 }
231
232 /************************************************************************/
233 /* Tell() */
234 /************************************************************************/
235
Tell()236 vsi_l_offset VSIStdinHandle::Tell()
237 {
238 return nCurOff;
239 }
240
241 /************************************************************************/
242 /* Read() */
243 /************************************************************************/
244
Read(void * pBuffer,size_t nSize,size_t nCount)245 size_t VSIStdinHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
246
247 {
248 VSIStdinInit();
249
250 if( nCurOff < nBufferLen )
251 {
252 if( nCurOff + nSize * nCount < nBufferLen )
253 {
254 memcpy(pBuffer, pabyBuffer + nCurOff, nSize * nCount);
255 nCurOff += nSize * nCount;
256 return nCount;
257 }
258
259 const int nAlreadyCached = static_cast<int>(nBufferLen - nCurOff);
260 memcpy(pBuffer, pabyBuffer + nCurOff, nAlreadyCached);
261
262 nCurOff += nAlreadyCached;
263
264 const int nRead =
265 ReadAndCache( static_cast<GByte *>(pBuffer) + nAlreadyCached,
266 static_cast<int>(nSize*nCount - nAlreadyCached) );
267
268 return (nRead + nAlreadyCached) / nSize;
269 }
270
271 int nRead = ReadAndCache( pBuffer, static_cast<int>(nSize * nCount) );
272 return nRead / nSize;
273 }
274
275 /************************************************************************/
276 /* Write() */
277 /************************************************************************/
278
Write(const void *,size_t,size_t)279 size_t VSIStdinHandle::Write( const void * /* pBuffer */,
280 size_t /* nSize */,
281 size_t /* nCount */ )
282 {
283 CPLError(CE_Failure, CPLE_NotSupported,
284 "Write() unsupported on /vsistdin");
285 return 0;
286 }
287
288 /************************************************************************/
289 /* Eof() */
290 /************************************************************************/
291
Eof()292 int VSIStdinHandle::Eof()
293
294 {
295 if( nCurOff < nBufferLen )
296 return FALSE;
297 return feof(stdin);
298 }
299
300 /************************************************************************/
301 /* Close() */
302 /************************************************************************/
303
Close()304 int VSIStdinHandle::Close()
305
306 {
307 return 0;
308 }
309
310 /************************************************************************/
311 /* ==================================================================== */
312 /* VSIStdinFilesystemHandler */
313 /* ==================================================================== */
314 /************************************************************************/
315
316 /************************************************************************/
317 /* VSIStdinFilesystemHandler() */
318 /************************************************************************/
319
VSIStdinFilesystemHandler()320 VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
321 {
322 pabyBuffer = nullptr;
323 nBufferLen = 0;
324 nRealPos = 0;
325 }
326
327 /************************************************************************/
328 /* ~VSIStdinFilesystemHandler() */
329 /************************************************************************/
330
~VSIStdinFilesystemHandler()331 VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler()
332 {
333 CPLFree(pabyBuffer);
334 pabyBuffer = nullptr;
335 }
336
337 /************************************************************************/
338 /* Open() */
339 /************************************************************************/
340
341 VSIVirtualHandle *
Open(const char * pszFilename,const char * pszAccess,bool,CSLConstList)342 VSIStdinFilesystemHandler::Open( const char *pszFilename,
343 const char *pszAccess,
344 bool /* bSetError */,
345 CSLConstList /* papszOptions */ )
346
347 {
348 if( strcmp(pszFilename, "/vsistdin/") != 0 )
349 return nullptr;
350
351 if( !CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")) )
352 {
353 CPLError(CE_Failure, CPLE_NotSupported,
354 "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
355 "enable it");
356 return nullptr;
357 }
358
359 if( strchr(pszAccess, 'w') != nullptr ||
360 strchr(pszAccess, '+') != nullptr )
361 {
362 CPLError(CE_Failure, CPLE_NotSupported,
363 "Write or update mode not supported on /vsistdin");
364 return nullptr;
365 }
366
367 return new VSIStdinHandle;
368 }
369
370 /************************************************************************/
371 /* Stat() */
372 /************************************************************************/
373
Stat(const char * pszFilename,VSIStatBufL * pStatBuf,int nFlags)374 int VSIStdinFilesystemHandler::Stat( const char * pszFilename,
375 VSIStatBufL * pStatBuf,
376 int nFlags )
377
378 {
379 memset( pStatBuf, 0, sizeof(VSIStatBufL) );
380
381 if( strcmp(pszFilename, "/vsistdin/") != 0 )
382 return -1;
383
384 if( !CPLTestBool(CPLGetConfigOption("CPL_ALLOW_VSISTDIN", "YES")) )
385 {
386 CPLError(CE_Failure, CPLE_NotSupported,
387 "/vsistdin/ disabled. Set CPL_ALLOW_VSISTDIN to YES to "
388 "enable it");
389 return -1;
390 }
391
392 if( nFlags & VSI_STAT_SIZE_FLAG )
393 {
394 VSIStdinInit();
395 if( nBufferLen == 0 )
396 nRealPos = nBufferLen =
397 static_cast<int>(fread(pabyBuffer, 1, BUFFER_SIZE, stdin));
398
399 pStatBuf->st_size = nBufferLen;
400 }
401
402 pStatBuf->st_mode = S_IFREG;
403 return 0;
404 }
405
406 //! @endcond
407
408 /************************************************************************/
409 /* VSIInstallStdinHandler() */
410 /************************************************************************/
411
412 /**
413 * \brief Install /vsistdin/ file system handler
414 *
415 * A special file handler is installed that allows reading from the standard
416 * input stream.
417 *
418 * The file operations available are of course limited to Read() and
419 * forward Seek() (full seek in the first MB of a file).
420 *
421 * @since GDAL 1.8.0
422 */
VSIInstallStdinHandler()423 void VSIInstallStdinHandler()
424
425 {
426 VSIFileManager::InstallHandler("/vsistdin/", new VSIStdinFilesystemHandler);
427 }
428