1 : /**********************************************************************
2 : * $Id: cpl_vsil_stdin.cpp 24432 2012-05-17 16:20:57Z rouault $
3 : *
4 : * Project: CPL - Common Portability Library
5 : * Purpose: Implement VSI large file api for stdin
6 : * Author: Even Rouault, <even dot rouault at mines dash paris dot org>
7 : *
8 : **********************************************************************
9 : * Copyright (c) 2010, Even Rouault
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "cpl_port.h"
31 : #include "cpl_error.h"
32 : #include "cpl_vsi_virtual.h"
33 :
34 : #include <stdio.h>
35 : #ifdef WIN32
36 : #include <io.h>
37 : #include <fcntl.h>
38 : #endif
39 :
40 : CPL_CVSID("$Id: cpl_vsil_stdin.cpp 24432 2012-05-17 16:20:57Z rouault $");
41 :
42 : /* We buffer the first 1MB of standard input to enable drivers */
43 : /* to autodetect data. In the first MB, backward and forward seeking */
44 : /* is allowed, after only forward seeking will work */
45 : #define BUFFER_SIZE (1024 * 1024)
46 :
47 : static GByte* pabyBuffer;
48 : static GUInt32 nBufferLen;
49 : static GUIntBig nRealPos;
50 :
51 : /************************************************************************/
52 : /* VSIStdinInit() */
53 : /************************************************************************/
54 :
55 545 : static void VSIStdinInit()
56 : {
57 545 : if (pabyBuffer == NULL)
58 : {
59 : #ifdef WIN32
60 : setmode( fileno( stdin ), O_BINARY );
61 : #endif
62 2 : pabyBuffer = (GByte*)CPLMalloc(BUFFER_SIZE);
63 : }
64 545 : }
65 :
66 : /************************************************************************/
67 : /* ==================================================================== */
68 : /* VSIStdinFilesystemHandler */
69 : /* ==================================================================== */
70 : /************************************************************************/
71 :
72 : class VSIStdinFilesystemHandler : public VSIFilesystemHandler
73 : {
74 : public:
75 : VSIStdinFilesystemHandler();
76 : virtual ~VSIStdinFilesystemHandler();
77 :
78 : virtual VSIVirtualHandle *Open( const char *pszFilename,
79 : const char *pszAccess);
80 : virtual int Stat( const char *pszFilename,
81 : VSIStatBufL *pStatBuf, int nFlags );
82 : };
83 :
84 : /************************************************************************/
85 : /* ==================================================================== */
86 : /* VSIStdinHandle */
87 : /* ==================================================================== */
88 : /************************************************************************/
89 :
90 : class VSIStdinHandle : public VSIVirtualHandle
91 : {
92 : private:
93 : GUIntBig nCurOff;
94 : int ReadAndCache( void* pBuffer, int nToRead );
95 :
96 : public:
97 : VSIStdinHandle();
98 : virtual ~VSIStdinHandle();
99 :
100 : virtual int Seek( vsi_l_offset nOffset, int nWhence );
101 : virtual vsi_l_offset Tell();
102 : virtual size_t Read( void *pBuffer, size_t nSize, size_t nMemb );
103 : virtual size_t Write( const void *pBuffer, size_t nSize, size_t nMemb );
104 : virtual int Eof();
105 : virtual int Close();
106 : };
107 :
108 : /************************************************************************/
109 : /* VSIStdinHandle() */
110 : /************************************************************************/
111 :
112 5 : VSIStdinHandle::VSIStdinHandle()
113 : {
114 5 : nCurOff = 0;
115 5 : }
116 :
117 : /************************************************************************/
118 : /* ~VSIStdinHandle() */
119 : /************************************************************************/
120 :
121 5 : VSIStdinHandle::~VSIStdinHandle()
122 : {
123 5 : }
124 :
125 :
126 : /************************************************************************/
127 : /* ReadAndCache() */
128 : /************************************************************************/
129 :
130 519 : int VSIStdinHandle::ReadAndCache( void* pBuffer, int nToRead )
131 : {
132 519 : CPLAssert(nCurOff == nRealPos);
133 :
134 519 : int nRead = fread(pBuffer, 1, nToRead, stdin);
135 :
136 519 : if (nRealPos < BUFFER_SIZE)
137 : {
138 135 : int nToCopy = MIN(BUFFER_SIZE - (int)nRealPos, nRead);
139 135 : memcpy(pabyBuffer + nRealPos, pBuffer, nToCopy);
140 135 : nBufferLen += nToCopy;
141 : }
142 :
143 519 : nCurOff += nRead;
144 519 : nRealPos = nCurOff;
145 :
146 519 : return nRead;
147 : }
148 :
149 : /************************************************************************/
150 : /* Seek() */
151 : /************************************************************************/
152 :
153 530 : int VSIStdinHandle::Seek( vsi_l_offset nOffset, int nWhence )
154 :
155 : {
156 530 : if (nWhence == SEEK_SET && nOffset == nCurOff)
157 522 : return 0;
158 :
159 8 : VSIStdinInit();
160 8 : if (nBufferLen == 0)
161 0 : nRealPos = nBufferLen = fread(pabyBuffer, 1, BUFFER_SIZE, stdin);
162 :
163 8 : if (nWhence == SEEK_END)
164 : {
165 2 : if (nOffset != 0)
166 : {
167 : CPLError(CE_Failure, CPLE_NotSupported,
168 0 : "Seek(xx != 0, SEEK_END) unsupported on /vsistdin");
169 0 : return -1;
170 : }
171 :
172 2 : if (nBufferLen < BUFFER_SIZE)
173 : {
174 2 : nCurOff = nBufferLen;
175 2 : return 0;
176 : }
177 :
178 : CPLError(CE_Failure, CPLE_NotSupported,
179 0 : "Seek(SEEK_END) unsupported on /vsistdin when stdin > 1 MB");
180 0 : return -1;
181 : }
182 :
183 6 : if (nWhence == SEEK_CUR)
184 0 : nOffset += nCurOff;
185 :
186 6 : if (nRealPos > nBufferLen && nOffset < nRealPos)
187 : {
188 : CPLError(CE_Failure, CPLE_NotSupported,
189 0 : "backward Seek() unsupported on /vsistdin above first MB");
190 0 : return -1;
191 : }
192 :
193 6 : if (nOffset < nBufferLen)
194 : {
195 4 : nCurOff = nOffset;
196 4 : return 0;
197 : }
198 :
199 2 : if (nOffset == nCurOff)
200 0 : return 0;
201 :
202 : CPLDebug("VSI", "Forward seek from " CPL_FRMT_GUIB " to " CPL_FRMT_GUIB,
203 2 : nCurOff, nOffset);
204 :
205 : char abyTemp[8192];
206 2 : nCurOff = nRealPos;
207 0 : while(TRUE)
208 : {
209 2 : int nToRead = (int) MIN(8192, nOffset - nCurOff);
210 2 : int nRead = ReadAndCache( abyTemp, nToRead );
211 :
212 2 : if (nRead < nToRead)
213 0 : return -1;
214 2 : if (nToRead < 8192)
215 : break;
216 : }
217 :
218 2 : return 0;
219 : }
220 :
221 : /************************************************************************/
222 : /* Tell() */
223 : /************************************************************************/
224 :
225 530 : vsi_l_offset VSIStdinHandle::Tell()
226 : {
227 530 : return nCurOff;
228 : }
229 :
230 : /************************************************************************/
231 : /* Read() */
232 : /************************************************************************/
233 :
234 537 : size_t VSIStdinHandle::Read( void * pBuffer, size_t nSize, size_t nCount )
235 :
236 : {
237 537 : VSIStdinInit();
238 :
239 537 : if (nCurOff < nBufferLen)
240 : {
241 22 : if (nCurOff + nSize * nCount < nBufferLen)
242 : {
243 20 : memcpy(pBuffer, pabyBuffer + nCurOff, nSize * nCount);
244 20 : nCurOff += nSize * nCount;
245 20 : return nCount;
246 : }
247 :
248 2 : int nAlreadyCached = (int)(nBufferLen - nCurOff);
249 2 : memcpy(pBuffer, pabyBuffer + nCurOff, nAlreadyCached);
250 :
251 2 : nCurOff += nAlreadyCached;
252 :
253 : int nRead = ReadAndCache( (GByte*)pBuffer + nAlreadyCached,
254 2 : (int)(nSize*nCount - nAlreadyCached) );
255 :
256 2 : return ((nRead + nAlreadyCached) / nSize);
257 : }
258 :
259 515 : int nRead = ReadAndCache( pBuffer, (int)(nSize * nCount) );
260 515 : return nRead / nSize;
261 : }
262 :
263 : /************************************************************************/
264 : /* Write() */
265 : /************************************************************************/
266 :
267 0 : size_t VSIStdinHandle::Write( const void * pBuffer, size_t nSize,
268 : size_t nCount )
269 :
270 : {
271 : CPLError(CE_Failure, CPLE_NotSupported,
272 0 : "Write() unsupported on /vsistdin");
273 0 : return 0;
274 : }
275 :
276 : /************************************************************************/
277 : /* Eof() */
278 : /************************************************************************/
279 :
280 0 : int VSIStdinHandle::Eof()
281 :
282 : {
283 0 : if (nCurOff < nBufferLen)
284 0 : return FALSE;
285 0 : return feof(stdin);
286 : }
287 :
288 : /************************************************************************/
289 : /* Close() */
290 : /************************************************************************/
291 :
292 5 : int VSIStdinHandle::Close()
293 :
294 : {
295 5 : return 0;
296 : }
297 :
298 : /************************************************************************/
299 : /* ==================================================================== */
300 : /* VSIStdinFilesystemHandler */
301 : /* ==================================================================== */
302 : /************************************************************************/
303 :
304 : /************************************************************************/
305 : /* VSIStdinFilesystemHandler() */
306 : /************************************************************************/
307 :
308 712 : VSIStdinFilesystemHandler::VSIStdinFilesystemHandler()
309 : {
310 712 : pabyBuffer = NULL;
311 712 : nBufferLen = 0;
312 712 : nRealPos = 0;
313 712 : }
314 :
315 : /************************************************************************/
316 : /* ~VSIStdinFilesystemHandler() */
317 : /************************************************************************/
318 :
319 687 : VSIStdinFilesystemHandler::~VSIStdinFilesystemHandler()
320 : {
321 687 : CPLFree(pabyBuffer);
322 687 : pabyBuffer = NULL;
323 687 : }
324 :
325 : /************************************************************************/
326 : /* Open() */
327 : /************************************************************************/
328 :
329 : VSIVirtualHandle *
330 24 : VSIStdinFilesystemHandler::Open( const char *pszFilename,
331 : const char *pszAccess )
332 :
333 : {
334 24 : if (strcmp(pszFilename, "/vsistdin/") != 0)
335 18 : return NULL;
336 :
337 6 : if ( strchr(pszAccess, 'w') != NULL ||
338 : strchr(pszAccess, '+') != NULL )
339 : {
340 : CPLError(CE_Failure, CPLE_NotSupported,
341 1 : "Write or update mode not supported on /vsistdin");
342 1 : return NULL;
343 : }
344 :
345 5 : return new VSIStdinHandle;
346 : }
347 :
348 : /************************************************************************/
349 : /* Stat() */
350 : /************************************************************************/
351 :
352 26 : int VSIStdinFilesystemHandler::Stat( const char * pszFilename,
353 : VSIStatBufL * pStatBuf,
354 : int nFlags )
355 :
356 : {
357 26 : memset( pStatBuf, 0, sizeof(VSIStatBufL) );
358 :
359 26 : if (strcmp(pszFilename, "/vsistdin/") != 0)
360 24 : return -1;
361 :
362 2 : if ((nFlags & VSI_STAT_SIZE_FLAG))
363 : {
364 0 : VSIStdinInit();
365 0 : if (nBufferLen == 0)
366 0 : nRealPos = nBufferLen = fread(pabyBuffer, 1, BUFFER_SIZE, stdin);
367 :
368 0 : pStatBuf->st_size = nBufferLen;
369 : }
370 :
371 2 : pStatBuf->st_mode = S_IFREG;
372 2 : return 0;
373 : }
374 :
375 : /************************************************************************/
376 : /* VSIInstallStdinHandler() */
377 : /************************************************************************/
378 :
379 : /**
380 : * \brief Install /vsistdin/ file system handler
381 : *
382 : * A special file handler is installed that allows reading from the standard
383 : * input steam.
384 : *
385 : * The file operations available are of course limited to Read() and
386 : * forward Seek() (full seek in the first MB of a file).
387 : *
388 : * @since GDAL 1.8.0
389 : */
390 712 : void VSIInstallStdinHandler()
391 :
392 : {
393 712 : VSIFileManager::InstallHandler( "/vsistdin/", new VSIStdinFilesystemHandler );
394 712 : }
|