1 : /******************************************************************************
2 : * $Id: ogrgpsbabeldatasource.cpp 19893 2010-06-19 09:00:16Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRGPSBabelDataSource class.
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
22 : * OR 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 <string.h>
31 : #include "ogr_gpsbabel.h"
32 : #include "cpl_conv.h"
33 : #include "cpl_string.h"
34 : #include "cpl_error.h"
35 :
36 : CPL_CVSID("$Id: ogrgpsbabeldatasource.cpp 19893 2010-06-19 09:00:16Z rouault $");
37 :
38 : /************************************************************************/
39 : /* OGRGPSBabelDataSource() */
40 : /************************************************************************/
41 :
42 11 : OGRGPSBabelDataSource::OGRGPSBabelDataSource()
43 :
44 : {
45 11 : nLayers = 0;
46 11 : pszName = NULL;
47 11 : pszGPSBabelDriverName = NULL;
48 11 : pszFilename = NULL;
49 11 : poGPXDS = NULL;
50 11 : }
51 :
52 : /************************************************************************/
53 : /* ~OGRGPSBabelDataSource() */
54 : /************************************************************************/
55 :
56 11 : OGRGPSBabelDataSource::~OGRGPSBabelDataSource()
57 :
58 : {
59 11 : CPLFree(pszName);
60 11 : CPLFree(pszGPSBabelDriverName);
61 11 : CPLFree(pszFilename);
62 :
63 11 : if (poGPXDS)
64 2 : OGRDataSource::DestroyDataSource(poGPXDS);
65 :
66 11 : if (osTmpFileName.size() > 0)
67 2 : VSIUnlink(osTmpFileName.c_str());
68 11 : }
69 :
70 : /************************************************************************/
71 : /* GetArgv() */
72 : /************************************************************************/
73 :
74 : static char** GetArgv(int bExplicitFeatures, int bWaypoints, int bRoutes,
75 : int bTracks, const char* pszGPSBabelDriverName,
76 2 : const char* pszFilename)
77 : {
78 2 : char** argv = NULL;
79 2 : argv = CSLAddString(argv, "gpsbabel");
80 2 : if (bExplicitFeatures)
81 : {
82 0 : if (bWaypoints) argv = CSLAddString(argv, "-w");
83 0 : if (bRoutes) argv = CSLAddString(argv, "-r");
84 0 : if (bTracks) argv = CSLAddString(argv, "-t");
85 : }
86 2 : argv = CSLAddString(argv, "-i");
87 2 : argv = CSLAddString(argv, pszGPSBabelDriverName);
88 2 : argv = CSLAddString(argv, "-f");
89 2 : argv = CSLAddString(argv, pszFilename);
90 2 : argv = CSLAddString(argv, "-o");
91 2 : argv = CSLAddString(argv, "gpx,gpxver=1.1");
92 2 : argv = CSLAddString(argv, "-F");
93 2 : argv = CSLAddString(argv, "-");
94 :
95 2 : return argv;
96 : }
97 :
98 : /************************************************************************/
99 : /* IsSpecialFile() */
100 : /************************************************************************/
101 :
102 3 : int OGRGPSBabelDataSource::IsSpecialFile(const char* pszFilename)
103 : {
104 : return (strncmp(pszFilename, "/dev/", 5) == 0 ||
105 : strncmp(pszFilename, "usb:", 4) == 0 ||
106 3 : (strncmp(pszFilename, "COM", 3) == 0 && atoi(pszFilename + 3) > 0));
107 : }
108 :
109 : /************************************************************************/
110 : /* IsValidDriverName() */
111 : /************************************************************************/
112 :
113 2 : int OGRGPSBabelDataSource::IsValidDriverName(const char* pszGPSBabelDriverName)
114 : {
115 : int i;
116 10 : for(i=0;pszGPSBabelDriverName[i] != '\0';i++)
117 : {
118 8 : char ch = pszGPSBabelDriverName[i];
119 8 : if (!((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
120 : (ch >= '0' && ch <= '9') || ch == '_' || ch == '=' || ch == '.' || ch == ','))
121 : {
122 : CPLError(CE_Failure, CPLE_AppDefined,
123 0 : "Invalid GPSBabel driver name");
124 0 : return FALSE;
125 : }
126 : }
127 2 : return TRUE;
128 : }
129 :
130 : /************************************************************************/
131 : /* Open() */
132 : /************************************************************************/
133 :
134 11 : int OGRGPSBabelDataSource::Open( const char * pszDatasourceName, int bUpdateIn)
135 :
136 : {
137 11 : int bExplicitFeatures = FALSE;
138 11 : int bWaypoints = TRUE, bTracks = TRUE, bRoutes = TRUE;
139 11 : if (bUpdateIn)
140 : {
141 : CPLError(CE_Failure, CPLE_NotSupported,
142 0 : "OGR/GPSBabel driver does not support opening a file in update mode");
143 0 : return FALSE;
144 : }
145 :
146 11 : if (!EQUALN(pszDatasourceName, "GPSBABEL:", 9))
147 : {
148 : VSIStatBufL sStatBuf;
149 10 : if( VSIStatL( pszDatasourceName, &sStatBuf ) != 0
150 : ||!VSI_ISREG(sStatBuf.st_mode) )
151 2 : return FALSE;
152 :
153 8 : FILE* fp = VSIFOpenL(pszDatasourceName, "rb");
154 8 : if (fp == NULL)
155 0 : return FALSE;
156 :
157 : char szHeader[1024 + 1];
158 8 : memset(szHeader, 0, 1024+1);
159 8 : VSIFReadL(szHeader, 1, 1024, fp);
160 8 : if (memcmp(szHeader, "MsRcd", 5) == 0)
161 0 : pszGPSBabelDriverName = CPLStrdup("mapsource");
162 8 : else if (memcmp(szHeader, "MsRcf", 5) == 0)
163 0 : pszGPSBabelDriverName = CPLStrdup("gdb");
164 8 : else if (strstr(szHeader, "<osm") != NULL)
165 0 : pszGPSBabelDriverName = CPLStrdup("osm");
166 9 : else if (strstr(szHeader, "$GPGSA") != NULL ||
167 : strstr(szHeader, "$GPGGA") != NULL)
168 1 : pszGPSBabelDriverName = CPLStrdup("nmea");
169 7 : else if (EQUALN(szHeader, "OziExplorer",11))
170 0 : pszGPSBabelDriverName = CPLStrdup("ozi");
171 7 : else if (strstr(szHeader, "Grid") && strstr(szHeader, "Datum") && strstr(szHeader, "Header"))
172 0 : pszGPSBabelDriverName = CPLStrdup("garmin_txt");
173 7 : else if (szHeader[0] == 13 && szHeader[10] == 'M' && szHeader[11] == 'S' &&
174 : (szHeader[12] >= '0' && szHeader[12] <= '9') &&
175 : (szHeader[13] >= '0' && szHeader[13] <= '9') &&
176 : szHeader[12] * 10 + szHeader[13] >= 30 &&
177 : (szHeader[14] == 1 || szHeader[14] == 2) && szHeader[15] == 0 &&
178 : szHeader[16] == 0 && szHeader[17] == 0)
179 0 : pszGPSBabelDriverName = CPLStrdup("mapsend");
180 7 : else if (strstr(szHeader, "$PMGNWPL") != NULL ||
181 : strstr(szHeader, "$PMGNRTE") != NULL)
182 0 : pszGPSBabelDriverName = CPLStrdup("magellan");
183 :
184 8 : VSIFCloseL(fp);
185 :
186 8 : if (pszGPSBabelDriverName == NULL)
187 : {
188 7 : return FALSE;
189 : }
190 :
191 1 : pszFilename = CPLStrdup(pszDatasourceName);
192 : }
193 :
194 2 : pszName = CPLStrdup( pszDatasourceName );
195 :
196 2 : if (pszGPSBabelDriverName == NULL)
197 : {
198 1 : const char* pszSep = strchr(pszDatasourceName + 9, ':');
199 1 : if (pszSep == NULL)
200 : {
201 : CPLError(CE_Failure, CPLE_AppDefined,
202 0 : "Wrong syntax. Expected GPSBabel:driver_name:file_name");
203 0 : return FALSE;
204 : }
205 :
206 1 : pszGPSBabelDriverName = CPLStrdup(pszDatasourceName + 9);
207 1 : *(strchr(pszGPSBabelDriverName, ':')) = '\0';
208 :
209 : /* A bit of validation to avoid command line injection */
210 1 : if (!IsValidDriverName(pszGPSBabelDriverName))
211 0 : return FALSE;
212 :
213 : /* Parse optionnal features= option */
214 1 : if (EQUALN(pszSep+1, "features=", 9))
215 : {
216 0 : const char* pszNextSep = strchr(pszSep+1, ':');
217 0 : if (pszNextSep == NULL)
218 : {
219 : CPLError(CE_Failure, CPLE_AppDefined,
220 0 : "Wrong syntax. Expected GPSBabel:driver_name[,options]*:[features=waypoints,tracks,routes:]file_name");
221 0 : return FALSE;
222 : }
223 :
224 0 : char* pszFeatures = CPLStrdup(pszSep+1+9);
225 0 : *strchr(pszFeatures, ':') = 0;
226 0 : char** papszTokens = CSLTokenizeString(pszFeatures);
227 0 : char** papszIter = papszTokens;
228 0 : int bErr = FALSE;
229 0 : bExplicitFeatures = TRUE;
230 0 : bWaypoints = bTracks = bRoutes = FALSE;
231 0 : while(papszIter && *papszIter)
232 : {
233 0 : if (EQUAL(*papszIter, "waypoints"))
234 0 : bWaypoints = TRUE;
235 0 : else if (EQUAL(*papszIter, "tracks"))
236 0 : bTracks = TRUE;
237 0 : else if (EQUAL(*papszIter, "routes"))
238 0 : bRoutes = TRUE;
239 : else
240 : {
241 0 : CPLError(CE_Failure, CPLE_AppDefined, "Wrong value for 'features' options");
242 0 : bErr = TRUE;
243 : }
244 0 : papszIter ++;
245 : }
246 0 : CSLDestroy(papszTokens);
247 0 : CPLFree(pszFeatures);
248 :
249 0 : if (bErr)
250 0 : return FALSE;
251 :
252 0 : pszSep = pszNextSep;
253 : }
254 :
255 1 : pszFilename = CPLStrdup(pszSep+1);
256 : }
257 :
258 2 : const char* pszOptionUseTempFile = CPLGetConfigOption("USE_TEMPFILE", NULL);
259 2 : if (pszOptionUseTempFile && CSLTestBoolean(pszOptionUseTempFile))
260 0 : osTmpFileName = CPLGenerateTempFilename(NULL);
261 : else
262 2 : osTmpFileName.Printf("/vsimem/ogrgpsbabeldatasource_%p", this);
263 :
264 2 : int nRet = FALSE;
265 2 : if (IsSpecialFile(pszFilename))
266 : {
267 : /* Special file : don't try to open it */
268 : char** argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes,
269 0 : bTracks, pszGPSBabelDriverName, pszFilename);
270 0 : FILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
271 0 : nRet = ForkAndPipe(argv, NULL, tmpfp);
272 0 : VSIFCloseL(tmpfp);
273 0 : tmpfp = NULL;
274 0 : CSLDestroy(argv);
275 0 : argv = NULL;
276 : }
277 : else
278 : {
279 2 : FILE* fp = VSIFOpenL(pszFilename, "rb");
280 2 : if (fp == NULL)
281 : {
282 : CPLError(CE_Failure, CPLE_AppDefined,
283 0 : "Cannot open file %s", pszFilename);
284 0 : return FALSE;
285 : }
286 :
287 : char** argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes,
288 2 : bTracks, pszGPSBabelDriverName, "-");
289 :
290 2 : FILE* tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
291 :
292 2 : CPLPushErrorHandler(CPLQuietErrorHandler);
293 2 : nRet = ForkAndPipe(argv, fp, tmpfp);
294 2 : CPLPopErrorHandler();
295 :
296 2 : CSLDestroy(argv);
297 2 : argv = NULL;
298 :
299 2 : CPLErr nLastErrorType = CPLGetLastErrorType();
300 2 : int nLastErrorNo = CPLGetLastErrorNo();
301 2 : CPLString osLastErrorMsg = CPLGetLastErrorMsg();
302 :
303 2 : VSIFCloseL(tmpfp);
304 2 : tmpfp = NULL;
305 :
306 2 : VSIFCloseL(fp);
307 2 : fp = NULL;
308 :
309 2 : if (!nRet)
310 : {
311 0 : if (strstr(osLastErrorMsg.c_str(), "This format cannot be used in piped commands") == NULL)
312 : {
313 0 : CPLError(nLastErrorType, nLastErrorNo, "%s", osLastErrorMsg.c_str());
314 : }
315 : else
316 : {
317 : VSIStatBuf sStatBuf;
318 0 : if (VSIStat(pszFilename, &sStatBuf) != 0)
319 : {
320 : CPLError(CE_Failure, CPLE_NotSupported,
321 0 : "Driver %s only supports real (non virtual) files", pszGPSBabelDriverName);
322 0 : return FALSE;
323 : }
324 :
325 : /* Try without piping in */
326 : argv = GetArgv(bExplicitFeatures, bWaypoints, bRoutes,
327 0 : bTracks, pszGPSBabelDriverName, pszFilename);
328 0 : tmpfp = VSIFOpenL(osTmpFileName.c_str(), "wb");
329 0 : nRet = ForkAndPipe(argv, NULL, tmpfp);
330 0 : VSIFCloseL(tmpfp);
331 0 : tmpfp = NULL;
332 :
333 0 : CSLDestroy(argv);
334 0 : argv = NULL;
335 : }
336 0 : }
337 : }
338 :
339 :
340 2 : if (nRet)
341 : {
342 2 : poGPXDS = OGRSFDriverRegistrar::Open(osTmpFileName.c_str());
343 2 : if (poGPXDS)
344 : {
345 : OGRLayer* poLayer;
346 :
347 2 : if (bWaypoints)
348 : {
349 2 : poLayer = poGPXDS->GetLayerByName("waypoints");
350 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
351 2 : apoLayers[nLayers++] = poLayer;
352 : }
353 :
354 2 : if (bRoutes)
355 : {
356 2 : poLayer = poGPXDS->GetLayerByName("routes");
357 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
358 0 : apoLayers[nLayers++] = poLayer;
359 2 : poLayer = poGPXDS->GetLayerByName("route_points");
360 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
361 0 : apoLayers[nLayers++] = poLayer;
362 : }
363 :
364 2 : if (bTracks)
365 : {
366 2 : poLayer = poGPXDS->GetLayerByName("tracks");
367 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
368 2 : apoLayers[nLayers++] = poLayer;
369 2 : poLayer = poGPXDS->GetLayerByName("track_points");
370 2 : if (poLayer != NULL && poLayer->GetFeatureCount() != 0)
371 0 : apoLayers[nLayers++] = poLayer;
372 : }
373 : }
374 : }
375 :
376 2 : return nLayers > 0;
377 : }
378 :
379 : /************************************************************************/
380 : /* TestCapability() */
381 : /************************************************************************/
382 :
383 0 : int OGRGPSBabelDataSource::TestCapability( const char * pszCap )
384 :
385 : {
386 0 : return FALSE;
387 : }
388 :
389 : /************************************************************************/
390 : /* GetLayer() */
391 : /************************************************************************/
392 :
393 0 : OGRLayer *OGRGPSBabelDataSource::GetLayer( int iLayer )
394 :
395 : {
396 0 : if( iLayer < 0 || iLayer >= nLayers )
397 0 : return NULL;
398 : else
399 0 : return apoLayers[iLayer];
400 : }
|